Merge inbound to m-c.
FIREFOX_AURORA_22_BASE
Merge inbound to m-c.
--- a/accessible/src/base/TextAttrs.cpp
+++ b/accessible/src/base/TextAttrs.cpp
@@ -1,15 +1,16 @@
/* -*- Mode: C++; 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/. */
#include "TextAttrs.h"
+#include "Accessible-inl.h"
#include "HyperTextAccessibleWrap.h"
#include "nsAccUtils.h"
#include "nsCoreUtils.h"
#include "StyleInfo.h"
#include "gfxFont.h"
#include "gfxUserFontSet.h"
#include "nsFontMetrics.h"
--- a/accessible/src/base/nsAccUtils.cpp
+++ b/accessible/src/base/nsAccUtils.cpp
@@ -234,17 +234,17 @@ nsAccUtils::GetSelectableContainer(Acces
if (!aAccessible)
return nullptr;
if (!(aState & states::SELECTABLE))
return nullptr;
Accessible* parent = aAccessible;
while ((parent = parent->Parent()) && !parent->IsSelect()) {
- if (Role(parent) == nsIAccessibleRole::ROLE_PANE)
+ if (parent->Role() == roles::PANE)
return nullptr;
}
return parent;
}
bool
nsAccUtils::IsARIASelected(Accessible* aAccessible)
{
--- a/accessible/src/base/nsAccUtils.h
+++ b/accessible/src/base/nsAccUtils.h
@@ -1,18 +1,17 @@
/* -*- Mode: C++; 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/. */
#ifndef nsAccUtils_h_
#define nsAccUtils_h_
-#include "nsIAccessible.h"
-#include "nsIAccessibleRole.h"
+#include "mozilla/a11y/Accessible.h"
#include "nsIAccessibleText.h"
#include "nsAccessibilityService.h"
#include "nsCoreUtils.h"
#include "mozilla/dom/Element.h"
#include "nsIDocShell.h"
#include "nsIPersistentProperties2.h"
@@ -188,28 +187,16 @@ public:
/**
* Returns coordinates relative screen for the parent of the given accessible.
*
* @param [in] aAccessible the accessible
*/
static nsIntPoint GetScreenCoordsForParent(Accessible* aAccessible);
/**
- * Return the role of the given accessible.
- */
- static uint32_t Role(nsIAccessible *aAcc)
- {
- uint32_t role = nsIAccessibleRole::ROLE_NOTHING;
- if (aAcc)
- aAcc->GetRole(&role);
-
- return role;
- }
-
- /**
* Get the ARIA attribute characteristics for a given ARIA attribute.
*
* @param aAtom ARIA attribute
* @return A bitflag representing the attribute characteristics
* (see nsARIAMap.h for possible bit masks, prefixed "ARIA_")
*/
static uint8_t GetAttributeCharacteristics(nsIAtom* aAtom);
@@ -235,22 +222,22 @@ public:
/**
* Return text length of the given accessible, return 0 on failure.
*/
static uint32_t TextLength(Accessible* aAccessible);
/**
* Return true if the given accessible is embedded object.
*/
- static bool IsEmbeddedObject(nsIAccessible *aAcc)
+ static bool IsEmbeddedObject(Accessible* aAcc)
{
- uint32_t role = Role(aAcc);
- return role != nsIAccessibleRole::ROLE_TEXT_LEAF &&
- role != nsIAccessibleRole::ROLE_WHITESPACE &&
- role != nsIAccessibleRole::ROLE_STATICTEXT;
+ uint32_t role = aAcc->Role();
+ return role != roles::TEXT_LEAF &&
+ role != roles::WHITESPACE &&
+ role != roles::STATICTEXT;
}
/**
* Transform nsIAccessibleStates constants to internal state constant.
*/
static inline uint64_t To64State(uint32_t aState1, uint32_t aState2)
{
return static_cast<uint64_t>(aState1) +
--- a/accessible/src/base/nsAccessibilityService.cpp
+++ b/accessible/src/base/nsAccessibilityService.cpp
@@ -32,16 +32,17 @@
#include "Statistics.h"
#include "TextLeafAccessibleWrap.h"
#ifdef MOZ_ACCESSIBILITY_ATK
#include "AtkSocketAccessible.h"
#endif
#ifdef XP_WIN
+#include "mozilla/a11y/Compatibility.h"
#include "HTMLWin32ObjectAccessible.h"
#endif
#ifdef A11Y_LOG
#include "Logging.h"
#endif
#ifdef MOZ_CRASHREPORTER
@@ -58,16 +59,17 @@
#include "nsObjectFrame.h"
#include "nsSVGPathGeometryFrame.h"
#include "nsTreeBodyFrame.h"
#include "nsTreeColumns.h"
#include "nsTreeUtils.h"
#include "mozilla/dom/Element.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
+#include "mozilla/StaticPtr.h"
#include "mozilla/Util.h"
#include "nsDeckFrame.h"
#ifdef MOZ_XUL
#include "XULAlertAccessible.h"
#include "XULColorPickerAccessible.h"
#include "XULComboboxAccessible.h"
#include "XULElementAccessibles.h"
@@ -205,31 +207,87 @@ nsAccessibilityService::GetRootDocumentA
}
return aCanCreate ? GetDocAccessible(ps) : ps->GetDocAccessible();
}
}
return nullptr;
}
+#ifdef XP_WIN
+static StaticAutoPtr<nsTArray<nsCOMPtr<nsIContent> > > sPendingPlugins;
+static StaticAutoPtr<nsTArray<nsCOMPtr<nsITimer> > > sPluginTimers;
+
+class PluginTimerCallBack MOZ_FINAL : public nsITimerCallback
+{
+public:
+ PluginTimerCallBack(nsIContent* aContent) : mContent(aContent) {}
+
+ NS_DECL_ISUPPORTS
+
+ NS_IMETHODIMP Notify(nsITimer* aTimer) MOZ_FINAL
+ {
+ nsIPresShell* ps = mContent->OwnerDoc()->GetShell();
+ if (ps) {
+ DocAccessible* doc = ps->GetDocAccessible();
+ if (doc) {
+ // Make sure that if we created an accessible for the plugin that wasn't
+ // a plugin accessible we remove it before creating the right accessible.
+ doc->RecreateAccessible(mContent);
+ sPluginTimers->RemoveElement(aTimer);
+ return NS_OK;
+ }
+ }
+
+ // We couldn't get a doc accessible so presumably the document went away.
+ // In this case don't leak our ref to the content or timer.
+ sPendingPlugins->RemoveElement(mContent);
+ sPluginTimers->RemoveElement(aTimer);
+ return NS_OK;
+ }
+
+private:
+ nsCOMPtr<nsIContent> mContent;
+};
+
+NS_IMPL_ISUPPORTS1(PluginTimerCallBack, nsITimerCallback)
+#endif
+
already_AddRefed<Accessible>
nsAccessibilityService::CreatePluginAccessible(nsObjectFrame* aFrame,
nsIContent* aContent,
Accessible* aContext)
{
// nsObjectFrame means a plugin, so we need to use the accessibility support
// of the plugin.
if (aFrame->GetRect().IsEmpty())
return nullptr;
#if defined(XP_WIN) || defined(MOZ_ACCESSIBILITY_ATK)
nsRefPtr<nsNPAPIPluginInstance> pluginInstance;
if (NS_SUCCEEDED(aFrame->GetPluginInstance(getter_AddRefs(pluginInstance))) &&
pluginInstance) {
#ifdef XP_WIN
+ if (!sPendingPlugins->Contains(aContent) &&
+ (Preferences::GetBool("accessibility.delay_plugins") ||
+ Compatibility::IsJAWS() || Compatibility::IsWE())) {
+ nsCOMPtr<nsITimer> timer = do_CreateInstance(NS_TIMER_CONTRACTID);
+ nsRefPtr<PluginTimerCallBack> cb = new PluginTimerCallBack(aContent);
+ timer->InitWithCallback(cb, Preferences::GetUint("accessibility.delay_plugin_time"),
+ nsITimer::TYPE_ONE_SHOT);
+ sPluginTimers->AppendElement(timer);
+ sPendingPlugins->AppendElement(aContent);
+ return nullptr;
+ }
+
+ // We need to remove aContent from the pending plugins here to avoid
+ // reentrancy. When the timer fires it calls
+ // DocAccessible::ContentInserted() which does the work async.
+ sPendingPlugins->RemoveElement(aContent);
+
// Note: pluginPort will be null if windowless.
HWND pluginPort = nullptr;
aFrame->GetPluginPort(&pluginPort);
Accessible* accessible =
new HTMLWin32ObjectOwnerAccessible(aContent, aContext->Document(),
pluginPort);
NS_ADDREF(accessible);
@@ -999,16 +1057,21 @@ nsAccessibilityService::Init()
NS_ADDREF(gApplicationAccessible); // will release in Shutdown()
#ifdef MOZ_CRASHREPORTER
CrashReporter::
AnnotateCrashReport(NS_LITERAL_CSTRING("Accessibility"),
NS_LITERAL_CSTRING("Active"));
#endif
+#ifdef XP_WIN
+ sPendingPlugins = new nsTArray<nsCOMPtr<nsIContent> >;
+ sPluginTimers = new nsTArray<nsCOMPtr<nsITimer> >;
+#endif
+
gIsShutdown = false;
// Now its safe to start platform accessibility.
PlatformInit();
return true;
}
@@ -1025,16 +1088,26 @@ nsAccessibilityService::Shutdown()
observerService->NotifyObservers(nullptr, "a11y-init-or-shutdown", kShutdownIndicator);
}
// Stop accessible document loader.
DocManager::Shutdown();
SelectionManager::Shutdown();
+#ifdef XP_WIN
+ sPendingPlugins = nullptr;
+
+ uint32_t timerCount = sPluginTimers->Length();
+ for (uint32_t i = 0; i < timerCount; i++)
+ sPluginTimers->ElementAt(i)->Cancel();
+
+ sPluginTimers = nullptr;
+#endif
+
// Application is going to be closed, shutdown accessibility and mark
// accessibility service as shutdown to prevent calls of its methods.
// Don't null accessibility service static member at this point to be safe
// if someone will try to operate with it.
NS_ASSERTION(!gIsShutdown, "Accessibility was shutdown already");
gIsShutdown = true;
--- a/accessible/src/generic/Accessible.cpp
+++ b/accessible/src/generic/Accessible.cpp
@@ -9,16 +9,17 @@
#include "AccCollector.h"
#include "AccGroupInfo.h"
#include "AccIterator.h"
#include "nsAccUtils.h"
#include "nsAccessibleRelation.h"
#include "nsAccessibilityService.h"
#include "nsIAccessibleRelation.h"
+#include "nsIAccessibleRole.h"
#include "nsEventShell.h"
#include "nsTextEquivUtils.h"
#include "Relation.h"
#include "Role.h"
#include "RootAccessible.h"
#include "States.h"
#include "StyleInfo.h"
#include "TableAccessible.h"
@@ -2649,17 +2650,18 @@ Accessible::InsertChildAt(uint32_t aInde
if (!mChildren.AppendElement(aChild))
return false;
} else {
if (!mChildren.InsertElementAt(aIndex, aChild))
return false;
for (uint32_t idx = aIndex + 1; idx < mChildren.Length(); idx++) {
- NS_ASSERTION(mChildren[idx]->mIndexInParent == idx - 1, "Accessible child index doesn't match");
+ NS_ASSERTION(static_cast<uint32_t>(mChildren[idx]->mIndexInParent) == idx - 1,
+ "Accessible child index doesn't match");
mChildren[idx]->mIndexInParent = idx;
}
mEmbeddedObjCollector = nullptr;
}
if (!nsAccUtils::IsEmbeddedObject(aChild))
SetChildrenFlag(eMixedChildren);
@@ -2680,17 +2682,18 @@ Accessible::RemoveChild(Accessible* aChi
uint32_t index = static_cast<uint32_t>(aChild->mIndexInParent);
if (index >= mChildren.Length() || mChildren[index] != aChild) {
NS_ERROR("Child is bound to parent but parent hasn't this child at its index!");
aChild->UnbindFromParent();
return false;
}
for (uint32_t idx = index + 1; idx < mChildren.Length(); idx++) {
- NS_ASSERTION(mChildren[idx]->mIndexInParent == idx, "Accessible child index doesn't match");
+ NS_ASSERTION(static_cast<uint32_t>(mChildren[idx]->mIndexInParent) == idx,
+ "Accessible child index doesn't match");
mChildren[idx]->mIndexInParent = idx - 1;
}
aChild->UnbindFromParent();
mChildren.RemoveElementAt(index);
mEmbeddedObjCollector = nullptr;
return true;
--- a/accessible/src/generic/HyperTextAccessible.cpp
+++ b/accessible/src/generic/HyperTextAccessible.cpp
@@ -776,16 +776,47 @@ HyperTextAccessible::GetRelativeOffset(n
if (!aNeedsStart) {
-- hyperTextOffset;
}
}
return hyperTextOffset;
}
+int32_t
+HyperTextAccessible::FindWordBoundary(int32_t aOffset, nsDirection aDirection,
+ EWordMovementType aWordMovementType)
+{
+ // Convert hypertext offset to frame-relative offset.
+ int32_t offsetInFrame = aOffset, notUsedOffset = aOffset;
+ nsRefPtr<Accessible> accAtOffset;
+ nsIFrame* frameAtOffset =
+ GetPosAndText(offsetInFrame, notUsedOffset, nullptr, nullptr,
+ nullptr, getter_AddRefs(accAtOffset));
+ if (!frameAtOffset) {
+ if (aOffset == CharacterCount()) {
+ // Asking for start of line, while on last character.
+ if (accAtOffset)
+ frameAtOffset = accAtOffset->GetFrame();
+ }
+ NS_ASSERTION(frameAtOffset, "No start frame for text getting!");
+ if (!frameAtOffset)
+ return -1;
+
+ // We're on the last continuation since we're on the last character.
+ frameAtOffset = frameAtOffset->GetLastContinuation();
+ }
+
+ // Return hypertext offset of the boundary of the found word.
+ return GetRelativeOffset(mDoc->PresShell(), frameAtOffset, offsetInFrame,
+ accAtOffset, eSelectWord, aDirection,
+ (aWordMovementType == eStartWord),
+ aWordMovementType);
+}
+
/*
Gets the specified text relative to aBoundaryType, which means:
BOUNDARY_CHAR The character before/at/after the offset is returned.
BOUNDARY_WORD_START From the word start before/at/after the offset to the next word start.
BOUNDARY_WORD_END From the word end before/at/after the offset to the next work end.
BOUNDARY_LINE_START From the line start before/at/after the offset to the next line start.
BOUNDARY_LINE_END From the line end before/at/after the offset to the next line start.
*/
@@ -968,145 +999,122 @@ NS_IMETHODIMP
HyperTextAccessible::GetTextBeforeOffset(int32_t aOffset,
AccessibleTextBoundary aBoundaryType,
int32_t* aStartOffset,
int32_t* aEndOffset, nsAString& aText)
{
if (IsDefunct())
return NS_ERROR_FAILURE;
- if (aBoundaryType == BOUNDARY_CHAR) {
- GetCharAt(aOffset, eGetBefore, aText, aStartOffset, aEndOffset);
- return NS_OK;
+ int32_t offset = ConvertMagicOffset(aOffset);
+ if (offset < 0)
+ return NS_ERROR_INVALID_ARG;
+
+ switch (aBoundaryType) {
+ case BOUNDARY_CHAR:
+ GetCharAt(offset, eGetBefore, aText, aStartOffset, aEndOffset);
+ return NS_OK;
+
+ case BOUNDARY_WORD_START: {
+ if (offset == 0) { // no word before 0 offset
+ *aStartOffset = *aEndOffset = 0;
+ return NS_OK;
+ }
+
+ // If the offset is a word start then move backward to find start offset
+ // (end offset is the given offset). Otherwise move backward twice to find
+ // both start and end offsets.
+ int32_t midOffset = FindWordBoundary(offset, eDirPrevious, eStartWord);
+ *aEndOffset = FindWordBoundary(midOffset, eDirNext, eStartWord);
+ if (*aEndOffset == offset) {
+ *aStartOffset = midOffset;
+ return GetText(*aStartOffset, *aEndOffset, aText);
+ }
+
+ *aStartOffset = FindWordBoundary(midOffset, eDirPrevious, eStartWord);
+ *aEndOffset = midOffset;
+ return GetText(*aStartOffset, *aEndOffset, aText);
+ }
+
+ case BOUNDARY_WORD_END: {
+ if (offset == 0) { // no word before 0 offset
+ *aStartOffset = *aEndOffset = 0;
+ return NS_OK;
+ }
+
+ // Move word backward twice to find start and end offsets.
+ *aEndOffset = FindWordBoundary(offset, eDirPrevious, eEndWord);
+ *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eEndWord);
+ return GetText(*aStartOffset, *aEndOffset, aText);
+ }
+
+ case BOUNDARY_LINE_START:
+ case BOUNDARY_LINE_END:
+ case BOUNDARY_ATTRIBUTE_RANGE:
+ return GetTextHelper(eGetBefore, aBoundaryType, aOffset,
+ aStartOffset, aEndOffset, aText);
+
+ default:
+ return NS_ERROR_INVALID_ARG;
}
-
- return GetTextHelper(eGetBefore, aBoundaryType, aOffset,
- aStartOffset, aEndOffset, aText);
}
NS_IMETHODIMP
HyperTextAccessible::GetTextAtOffset(int32_t aOffset,
AccessibleTextBoundary aBoundaryType,
int32_t* aStartOffset,
int32_t* aEndOffset, nsAString& aText)
{
if (IsDefunct())
return NS_ERROR_FAILURE;
- nsSelectionAmount selectionAmount = eSelectWord;
+ int32_t offset = ConvertMagicOffset(aOffset);
+ if (offset < 0)
+ return NS_ERROR_INVALID_ARG;
+
EWordMovementType wordMovementType = eDefaultBehavior;
- bool forwardBack = true;
+ bool moveForwardThenBack = true;
switch (aBoundaryType) {
case BOUNDARY_CHAR:
return GetCharAt(aOffset, eGetAt, aText, aStartOffset, aEndOffset) ?
NS_OK : NS_ERROR_INVALID_ARG;
- case BOUNDARY_WORD_START:
- wordMovementType = eStartWord;
- break;
+ case BOUNDARY_WORD_START: {
+ uint32_t textLen = CharacterCount();
+ if (offset == textLen) {
+ *aStartOffset = *aEndOffset = textLen;
+ return NS_OK;
+ }
- case BOUNDARY_WORD_END:
- wordMovementType = eEndWord;
- forwardBack = false;
- break;
+ *aEndOffset = FindWordBoundary(offset, eDirNext, eStartWord);
+ *aStartOffset = FindWordBoundary(*aEndOffset, eDirPrevious, eStartWord);
+ return GetText(*aStartOffset, *aEndOffset, aText);
+ }
+
+ case BOUNDARY_WORD_END: {
+ if (offset == 0) {
+ *aStartOffset = *aEndOffset = 0;
+ return NS_OK;
+ }
+
+ *aStartOffset = FindWordBoundary(offset, eDirPrevious, eEndWord);
+ *aEndOffset = FindWordBoundary(*aStartOffset, eDirNext, eEndWord);
+ return GetText(*aStartOffset, *aEndOffset, aText);
+ }
case BOUNDARY_LINE_START:
case BOUNDARY_LINE_END:
+ case BOUNDARY_ATTRIBUTE_RANGE:
return GetTextHelper(eGetAt, aBoundaryType, aOffset,
aStartOffset, aEndOffset, aText);
- break;
-
- case BOUNDARY_ATTRIBUTE_RANGE:
- {
- nsresult rv = GetTextAttributes(false, aOffset,
- aStartOffset, aEndOffset, nullptr);
- NS_ENSURE_SUCCESS(rv, rv);
-
- return GetText(*aStartOffset, *aEndOffset, aText);
- }
default:
return NS_ERROR_INVALID_ARG;
}
-
- int32_t offset = ConvertMagicOffset(aOffset);
- if (offset < 0)
- return NS_ERROR_INVALID_ARG;
-
- uint32_t textLen = CharacterCount();
- if (forwardBack) {
- if (offset == textLen) {
- *aStartOffset = *aEndOffset = textLen;
- return NS_OK;
- }
- } else {
- if (offset == 0) {
- *aStartOffset = *aEndOffset = 0;
- return NS_OK;
- }
- }
-
- // Convert offsets to frame-relative
- int32_t startOffset = offset, endOffset = offset;
- nsRefPtr<Accessible> startAcc;
- nsIFrame* startFrame = GetPosAndText(startOffset, endOffset, nullptr, nullptr,
- nullptr, getter_AddRefs(startAcc));
- if (!startFrame) {
- if (offset == textLen) {
- // Asking for start of line, while on last character
- if (startAcc)
- startFrame = startAcc->GetFrame();
- }
- NS_ASSERTION(startFrame, "No start frame for text getting!");
- if (!startFrame)
- return NS_ERROR_FAILURE;
-
- // We're on the last continuation since we're on the last character.
- startFrame = startFrame->GetLastContinuation();
- }
-
- offset = GetRelativeOffset(mDoc->PresShell(), startFrame, startOffset,
- startAcc, selectionAmount,
- (forwardBack ? eDirNext : eDirPrevious),
- forwardBack, wordMovementType);
-
- if (forwardBack)
- *aEndOffset = offset;
- else
- *aStartOffset = offset;
-
- startOffset = endOffset = offset;
- startFrame = GetPosAndText(startOffset, endOffset, nullptr, nullptr,
- nullptr, getter_AddRefs(startAcc));
- if (!startFrame) {
- if (offset == textLen) {
- // Asking for start of line, while on last character
- if (startAcc)
- startFrame = startAcc->GetFrame();
- }
- NS_ASSERTION(startFrame, "No start frame for text getting!");
- if (!startFrame)
- return NS_ERROR_FAILURE;
-
- // We're on the last continuation since we're on the last character.
- startFrame = startFrame->GetLastContinuation();
- }
-
- offset = GetRelativeOffset(mDoc->PresShell(), startFrame, startOffset,
- startAcc, selectionAmount,
- (forwardBack ? eDirPrevious : eDirNext),
- forwardBack, wordMovementType);
-
- if (forwardBack)
- *aStartOffset = offset;
- else
- *aEndOffset = offset;
-
- return GetText(*aStartOffset, *aEndOffset, aText);
}
NS_IMETHODIMP
HyperTextAccessible::GetTextAfterOffset(int32_t aOffset,
AccessibleTextBoundary aBoundaryType,
int32_t* aStartOffset,
int32_t* aEndOffset, nsAString& aText)
{
--- a/accessible/src/generic/HyperTextAccessible.h
+++ b/accessible/src/generic/HyperTextAccessible.h
@@ -258,16 +258,22 @@ protected:
int32_t caretOffset = -1;
GetCaretOffset(&caretOffset);
return caretOffset;
}
return aOffset;
}
+ /**
+ * Return an offset of the found word boundary.
+ */
+ int32_t FindWordBoundary(int32_t aOffset, nsDirection aDirection,
+ EWordMovementType aWordMovementType);
+
/*
* This does the work for nsIAccessibleText::GetText[At|Before|After]Offset
* @param aType, eGetBefore, eGetAt, eGetAfter
* @param aBoundaryType, char/word-start/word-end/line-start/line-end/paragraph/attribute
* @param aOffset, offset into the hypertext to start from
* @param *aStartOffset, the resulting start offset for the returned substring
* @param *aEndOffset, the resulting end offset for the returned substring
* @param aText, the resulting substring
--- a/accessible/tests/mochitest/text/test_multiline.html
+++ b/accessible/tests/mochitest/text/test_multiline.html
@@ -11,17 +11,17 @@
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../text.js"></script>
<script type="application/javascript">
function doTest()
{
- SimpleTest.expectAssertions(54);
+ SimpleTest.expectAssertions(44);
// __o__n__e__w__o__r__d__\n
// 0 1 2 3 4 5 6 7
// __\n
// 8
// __t__w__o__ __w__o__r__d__s__\n
// 9 10 11 12 13 14 15 16 17 18
@@ -192,106 +192,46 @@
testTextBeforeOffset(10, BOUNDARY_CHAR, "t", 9, 10,
"div", kOk, kOk, kOk,
"divbr", kOk, kOk, kOk,
"editable", kOk, kOk, kOk,
"editablebr", kOk, kOk, kOk,
"textarea", kOk, kOk, kOk);
// BOUNDARY_WORD_START
- testTextBeforeOffset(0, BOUNDARY_WORD_START, "", 0, 0,
- "div", kOk, kOk, kOk,
- "divbr", kOk, kOk, kOk,
- "editable", kOk, kOk, kOk,
- "editablebr", kOk, kOk, kOk,
- "textarea", kOk, kOk, kOk);
- testTextBeforeOffset(7, BOUNDARY_WORD_START, "", 0, 0,
- "div", kTodo, kOk, kTodo,
- "divbr", kTodo, kOk, kTodo,
- "editable", kTodo, kOk, kTodo,
- "editablebr", kTodo, kOk, kTodo,
- "textarea", kTodo, kOk, kTodo);
- testTextBeforeOffset(8, BOUNDARY_WORD_START, "", 0, 0,
- "div", kTodo, kOk, kTodo,
- "divbr", kTodo, kOk, kTodo,
- "editable", kTodo, kOk, kTodo,
- "editablebr", kTodo, kOk, kTodo,
- "textarea", kTodo, kOk, kTodo);
- testTextBeforeOffset(9, BOUNDARY_WORD_START, "oneword\n\n", 0, 9,
- "div", kOk, kOk, kOk,
- "divbr", kOk, kOk, kOk,
- "editable", kOk, kOk, kOk,
- "editablebr", kOk, kOk, kOk,
- "textarea", kOk, kOk, kOk);
- testTextBeforeOffset(13, BOUNDARY_WORD_START, "two ", 9, 13,
- "div", kOk, kOk, kOk,
- "divbr", kOk, kOk, kOk,
- "editable", kOk, kOk, kOk,
- "editablebr", kOk, kOk, kOk,
- "textarea", kOk, kOk, kOk);
- testTextBeforeOffset(18, BOUNDARY_WORD_START, "two ", 9, 13,
- "div", kTodo, kTodo, kTodo,
- "divbr", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "editablebr", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(19, BOUNDARY_WORD_START, "two ", 9, 13,
- "div", kTodo, kTodo, kTodo,
- "divbr", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "editablebr", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
+ testTextBeforeOffset(0, BOUNDARY_WORD_START, "", 0, 0, IDs);
+ testTextBeforeOffset(7, BOUNDARY_WORD_START, "", 0, 0, IDs);
+ testTextBeforeOffset(8, BOUNDARY_WORD_START, "", 0, 0, IDs);
+ testTextBeforeOffset(9, BOUNDARY_WORD_START, "oneword\n\n", 0, 9, IDs);
+ testTextBeforeOffset(13, BOUNDARY_WORD_START, "two ", 9, 13, IDs);
+ testTextBeforeOffset(18, BOUNDARY_WORD_START, "two ", 9, 13, IDs);
+ testTextBeforeOffset(19, BOUNDARY_WORD_START, "words\n", 13, 19, IDs);
// BOUNDARY_WORD_END
- testTextBeforeOffset(0, BOUNDARY_WORD_END, "", 0, 0,
+ testTextBeforeOffset(0, BOUNDARY_WORD_END, "", 0, 0, IDs);
+ testTextBeforeOffset(7, BOUNDARY_WORD_END, "", 0, 0, IDs);
+ testTextBeforeOffset(8, BOUNDARY_WORD_END, "oneword", 0, 7,
"div", kOk, kOk, kOk,
- "divbr", kOk, kOk, kOk,
- "editable", kOk, kOk, kOk,
- "editablebr", kOk, kOk, kOk,
- "textarea", kOk, kOk, kOk);
- testTextBeforeOffset(7, BOUNDARY_WORD_END, "", 0, 0,
- "div", kTodo, kOk, kTodo,
"divbr", kTodo, kOk, kTodo,
- "editable", kTodo, kOk, kTodo,
- "editablebr", kTodo, kOk, kTodo,
- "textarea", kTodo, kOk, kTodo);
- testTextBeforeOffset(8, BOUNDARY_WORD_END, "oneword", 0, 7,
- "div", kTodo, kTodo, kTodo,
- "divbr", kTodo, kOk, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "editablebr", kTodo, kOk, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(9, BOUNDARY_WORD_END, "oneword", 0, 7,
- "div", kTodo, kTodo, kTodo,
- "divbr", kTodo, kOk, kTodo,
- "editable", kTodo, kTodo, kTodo,
+ "editable", kOk, kOk, kOk,
"editablebr", kTodo, kOk, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(12, BOUNDARY_WORD_END, "oneword", 0, 7,
- "div", kTodo, kTodo, kTodo,
- "divbr", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "editablebr", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(13, BOUNDARY_WORD_END, "\n\ntwo", 7, 12,
- "div", kTodo, kTodo, kTodo,
+ "textarea", kOk, kOk, kOk);
+ testTextBeforeOffset(9, BOUNDARY_WORD_END, "oneword", 0, 7,
+ "div", kOk, kOk, kOk,
+ "divbr", kTodo, kOk, kTodo,
+ "editable", kOk, kOk, kOk,
+ "editablebr", kTodo, kOk, kTodo,
+ "textarea", kOk, kOk, kOk);
+ testTextBeforeOffset(12, BOUNDARY_WORD_END, "oneword", 0, 7, IDs);
+ testTextBeforeOffset(13, BOUNDARY_WORD_END, "\n\ntwo", 7, 12, IDs);
+ testTextBeforeOffset(18, BOUNDARY_WORD_END, "\n\ntwo", 7, 12, IDs);
+ testTextBeforeOffset(19, BOUNDARY_WORD_END, " words", 12, 18,
+ "div", kOk, kOk, kOk,
"divbr", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "editablebr", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(18, BOUNDARY_WORD_END, "\n\ntwo", 7, 12,
- "div", kTodo, kTodo, kTodo,
- "divbr", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "editablebr", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(19, BOUNDARY_WORD_END, " words", 13, 18,
- "div", kTodo, kTodo, kTodo,
- "divbr", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
+ "editable", kOk, kOk, kOk,
"editablebr", kTodo, kTodo, kTodo,
"textarea", kTodo, kTodo, kTodo);
// BOUNDARY_LINE_START
testTextBeforeOffset(0, BOUNDARY_LINE_START, "", 0, 0,
"div", kOk, kOk, kOk,
"divbr", kOk, kOk, kOk,
"editable", kOk, kOk, kOk,
@@ -518,16 +458,26 @@
</head>
<body>
<a target="_blank"
title="nsIAccessibleText getText related functions test in multiline text"
href="https://bugzilla.mozilla.org/show_bug.cgi?id=612331">
Bug 612331
</a>
+ <a target="_blank"
+ title="getTextAtOffset for word boundaries: beginning of a new life"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=853340">
+ Bug 853340
+ </a>
+ <a target="_blank"
+ title="getTextBeforeOffset for word boundaries: evolving"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=855732">
+ Bug 855732
+ </a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
<div id="div">oneword
two words
</div>
--- a/accessible/tests/mochitest/text/test_singleline.html
+++ b/accessible/tests/mochitest/text/test_singleline.html
@@ -7,19 +7,19 @@
<script type="application/javascript"
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../text.js"></script>
<script type="application/javascript">
if (navigator.platform.startsWith("Mac")) {
- SimpleTest.expectAssertions(0, 28);
+ SimpleTest.expectAssertions(0, 20);
} else {
- SimpleTest.expectAssertions(28);
+ SimpleTest.expectAssertions(20);
}
function doTest()
{
// __h__e__l__l__o__ __m__y__ __f__r__i__e__n__d__
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
////////////////////////////////////////////////////////////////////////
@@ -213,118 +213,38 @@
// BOUNDARY_CHAR
testCharBeforeOffset(IDs, 0, "", 0, 0);
testCharBeforeOffset(IDs, 1, "h", 0, 1);
testCharBeforeOffset(IDs, 14, "n", 13, 14);
testCharBeforeOffset(IDs, 15, "d", 14, 15);
// BOUNDARY_WORD_START
- testTextBeforeOffset(0, BOUNDARY_WORD_START, "", 0, 0,
- "input", kOk, kOk, kOk,
- "div", kOk, kOk, kOk,
- "editable", kOk, kOk, kOk,
- "textarea", kOk, kOk, kOk);
- testTextBeforeOffset(1, BOUNDARY_WORD_START, "", 0, 0,
- "input", kTodo, kOk, kTodo,
- "div", kTodo, kOk, kTodo,
- "editable", kTodo, kOk, kTodo,
- "textarea", kTodo, kOk, kTodo);
- testTextBeforeOffset(5, BOUNDARY_WORD_START, "", 0, 0,
- "input", kTodo, kOk, kTodo,
- "div", kTodo, kOk, kTodo,
- "editable", kTodo, kOk, kTodo,
- "textarea", kTodo, kOk, kTodo);
- testTextBeforeOffset(6, BOUNDARY_WORD_START, "hello ", 0, 6,
- "input", kOk, kOk, kOk,
- "div", kOk, kOk, kOk,
- "editable", kOk, kOk, kOk,
- "textarea", kOk, kOk, kOk);
- testTextBeforeOffset(7, BOUNDARY_WORD_START, "hello ", 0, 6,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(8, BOUNDARY_WORD_START, "hello ", 0, 6,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(9, BOUNDARY_WORD_START, "my ", 6, 9,
- "input", kOk, kOk, kOk,
- "div", kOk, kOk, kOk,
- "editable", kOk, kOk, kOk,
- "textarea", kOk, kOk, kOk);
- testTextBeforeOffset(10, BOUNDARY_WORD_START, "my ", 6, 9,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(14, BOUNDARY_WORD_START, "my ", 6, 9,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(15, BOUNDARY_WORD_START, "my ", 6, 9,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
+ testTextBeforeOffset(0, BOUNDARY_WORD_START, "", 0, 0, IDs);
+ testTextBeforeOffset(1, BOUNDARY_WORD_START, "", 0, 0, IDs);
+ testTextBeforeOffset(5, BOUNDARY_WORD_START, "", 0, 0, IDs);
+ testTextBeforeOffset(6, BOUNDARY_WORD_START, "hello ", 0, 6, IDs);
+ testTextBeforeOffset(7, BOUNDARY_WORD_START, "hello ", 0, 6, IDs);
+ testTextBeforeOffset(8, BOUNDARY_WORD_START, "hello ", 0, 6, IDs);
+ testTextBeforeOffset(9, BOUNDARY_WORD_START, "my ", 6, 9, IDs);
+ testTextBeforeOffset(10, BOUNDARY_WORD_START, "my ", 6, 9, IDs);
+ testTextBeforeOffset(14, BOUNDARY_WORD_START, "my ", 6, 9, IDs);
+ testTextBeforeOffset(15, BOUNDARY_WORD_START, "friend", 9, 15, IDs);
// BOUNDARY_WORD_END
- testTextBeforeOffset(0, BOUNDARY_WORD_END, "", 0, 0,
- "input", kOk, kOk, kOk,
- "div", kOk, kOk, kOk,
- "editable", kOk, kOk, kOk,
- "textarea", kOk, kOk, kOk);
- testTextBeforeOffset(1, BOUNDARY_WORD_END, "", 0, 0,
- "input", kTodo, kOk, kTodo,
- "div", kTodo, kOk, kTodo,
- "editable", kTodo, kOk, kTodo,
- "textarea", kTodo, kOk, kTodo);
- testTextBeforeOffset(5, BOUNDARY_WORD_END, "", 0, 0,
- "input", kTodo, kOk, kTodo,
- "div", kTodo, kOk, kTodo,
- "editable", kTodo, kOk, kTodo,
- "textarea", kTodo, kOk, kTodo);
- testTextBeforeOffset(6, BOUNDARY_WORD_END, "hello ", 0, 6,
- "input", kTodo, kTodo, kOk,
- "div", kTodo, kTodo, kOk,
- "editable", kTodo, kTodo, kOk,
- "textarea", kTodo, kTodo, kOk);
- testTextBeforeOffset(7, BOUNDARY_WORD_END, "hello ", 0, 6,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(8, BOUNDARY_WORD_END, "hello ", 0, 6,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(9, BOUNDARY_WORD_END, " my", 5, 8,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(10, BOUNDARY_WORD_END, " my", 5, 8,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(14, BOUNDARY_WORD_END, " my", 5, 8,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(15, BOUNDARY_WORD_END, " my", 5, 8,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
+ testTextBeforeOffset(0, BOUNDARY_WORD_END, "", 0, 0, IDs);
+ testTextBeforeOffset(1, BOUNDARY_WORD_END, "", 0, 0, IDs);
+ testTextBeforeOffset(5, BOUNDARY_WORD_END, "", 0, 0, IDs);
+ testTextBeforeOffset(6, BOUNDARY_WORD_END, "hello", 0, 5, IDs);
+ testTextBeforeOffset(7, BOUNDARY_WORD_END, "hello", 0, 5, IDs);
+ testTextBeforeOffset(8, BOUNDARY_WORD_END, "hello", 0, 5, IDs);
+ testTextBeforeOffset(9, BOUNDARY_WORD_END, " my", 5, 8, IDs);
+ testTextBeforeOffset(10, BOUNDARY_WORD_END, " my", 5, 8, IDs);
+ testTextBeforeOffset(14, BOUNDARY_WORD_END, " my", 5, 8, IDs);
+ testTextBeforeOffset(15, BOUNDARY_WORD_END, " my", 5, 8, IDs);
// BOUNDARY_LINE_START
testTextBeforeOffset(0, BOUNDARY_LINE_START, "", 0, 0,
"input", kOk, kOk, kOk,
"div", kOk, kOk, kOk,
"editable", kOk, kOk, kOk,
"textarea", kOk, kOk, kOk);
testTextBeforeOffset(1, BOUNDARY_LINE_START, "", 0, 0,
@@ -458,17 +378,29 @@
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
title="nsIAccessibleText getText related function tests for html:input,html:div and html:textarea"
- href="https://bugzilla.mozilla.org/show_bug.cgi?id=452769">Mozilla Bug 452769</a>
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=452769">
+ Bug 452769
+ </a>
+ <a target="_blank"
+ title="getTextAtOffset for word boundaries: beginning of a new life"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=853340">
+ Bug 853340
+ </a>
+ <a target="_blank"
+ title="getTextBeforeOffset for word boundaries: evolving"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=855732">
+ Bug 855732
+ </a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
</pre>
<input id="input" value="hello my friend"/>
<div id="div">hello my friend</div>
<div id="editable" contenteditable="true">hello my friend</div>
--- a/accessible/tests/mochitest/text/test_whitespaces.html
+++ b/accessible/tests/mochitest/text/test_whitespaces.html
@@ -9,19 +9,19 @@
src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript"
src="../common.js"></script>
<script type="application/javascript"
src="../text.js"></script>
<script type="application/javascript">
if (navigator.platform.startsWith("Mac")) {
- SimpleTest.expectAssertions(0, 11);
+ SimpleTest.expectAssertions(0, 3);
} else {
- SimpleTest.expectAssertions(11);
+ SimpleTest.expectAssertions(3);
}
function doTest()
{
// __B__r__a__v__e__ __S__i__r__ __ __R__o__b__i__n__ __ __ __r__a__n
// 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21
////////////////////////////////////////////////////////////////////////
@@ -193,173 +193,49 @@
testCharBeforeOffset(IDs, 1, "B", 0, 1);
testCharBeforeOffset(IDs, 6, " ", 5, 6);
testCharBeforeOffset(IDs, 10, " ", 9, 10);
testCharBeforeOffset(IDs, 11, " ", 10, 11);
testCharBeforeOffset(IDs, 17, " ", 16, 17);
testCharBeforeOffset(IDs, 19, " ", 18, 19);
// BOUNDARY_WORD_START
- testTextBeforeOffset(0, BOUNDARY_WORD_START, "", 0, 0,
- "input", kOk, kOk, kOk,
- "div", kOk, kOk, kOk,
- "editable", kOk, kOk, kOk,
- "textarea", kOk, kOk, kOk);
- testTextBeforeOffset(1, BOUNDARY_WORD_START, "", 0, 0,
- "input", kTodo, kOk, kTodo,
- "div", kTodo, kOk, kTodo,
- "editable", kTodo, kOk, kTodo,
- "textarea", kTodo, kOk, kTodo);
- testTextBeforeOffset(5, BOUNDARY_WORD_START, "", 0, 0,
- "input", kTodo, kOk, kTodo,
- "div", kTodo, kOk, kTodo,
- "editable", kTodo, kOk, kTodo,
- "textarea", kTodo, kOk, kTodo);
- testTextBeforeOffset(6, BOUNDARY_WORD_START, "Brave ", 0, 6,
- "input", kOk, kOk, kOk,
- "div", kOk, kOk, kOk,
- "editable", kOk, kOk, kOk,
- "textarea", kOk, kOk, kOk);
- testTextBeforeOffset(9, BOUNDARY_WORD_START, "Brave ", 0, 6,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(10, BOUNDARY_WORD_START, "Brave ", 0, 6,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(11, BOUNDARY_WORD_START, "Sir ", 6, 11,
- "input", kOk, kOk, kOk,
- "div", kOk, kOk, kOk,
- "editable", kOk, kOk, kOk,
- "textarea", kOk, kOk, kOk);
- testTextBeforeOffset(15, BOUNDARY_WORD_START, "Sir ", 6, 11,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(16, BOUNDARY_WORD_START, "Sir ", 6, 11,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(17, BOUNDARY_WORD_START, "Sir ", 6, 11,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(18, BOUNDARY_WORD_START, "Sir ", 6, 11,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(19, BOUNDARY_WORD_START, "Robin ", 11, 19,
- "input", kOk, kOk, kOk,
- "div", kOk, kOk, kOk,
- "editable", kOk, kOk, kOk,
- "textarea", kOk, kOk, kOk);
- testTextBeforeOffset(20, BOUNDARY_WORD_START, "Robin ", 11, 19,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(21, BOUNDARY_WORD_START, "Robin ", 11, 19,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
+ testTextBeforeOffset(0, BOUNDARY_WORD_START, "", 0, 0, IDs);
+ testTextBeforeOffset(1, BOUNDARY_WORD_START, "", 0, 0, IDs);
+ testTextBeforeOffset(5, BOUNDARY_WORD_START, "", 0, 0, IDs);
+ testTextBeforeOffset(6, BOUNDARY_WORD_START, "Brave ", 0, 6, IDs);
+ testTextBeforeOffset(9, BOUNDARY_WORD_START, "Brave ", 0, 6, IDs);
+ testTextBeforeOffset(10, BOUNDARY_WORD_START, "Brave ", 0, 6, IDs);
+ testTextBeforeOffset(11, BOUNDARY_WORD_START, "Sir ", 6, 11, IDs);
+ testTextBeforeOffset(15, BOUNDARY_WORD_START, "Sir ", 6, 11, IDs);
+ testTextBeforeOffset(16, BOUNDARY_WORD_START, "Sir ", 6, 11, IDs);
+ testTextBeforeOffset(17, BOUNDARY_WORD_START, "Sir ", 6, 11, IDs);
+ testTextBeforeOffset(18, BOUNDARY_WORD_START, "Sir ", 6, 11, IDs);
+ testTextBeforeOffset(19, BOUNDARY_WORD_START, "Robin ", 11, 19, IDs);
+ testTextBeforeOffset(20, BOUNDARY_WORD_START, "Robin ", 11, 19, IDs);
+ testTextBeforeOffset(21, BOUNDARY_WORD_START, "Robin ", 11, 19, IDs);
// BOUNDARY_WORD_END
- testTextBeforeOffset(0, BOUNDARY_WORD_END, "", 0, 0,
- "input", kOk, kOk, kOk,
- "div", kOk, kOk, kOk,
- "editable", kOk, kOk, kOk,
- "textarea", kOk, kOk, kOk);
- testTextBeforeOffset(1, BOUNDARY_WORD_END, "", 0, 0,
- "input", kTodo, kOk, kTodo,
- "div", kTodo, kOk, kTodo,
- "editable", kTodo, kOk, kTodo,
- "textarea", kTodo, kOk, kTodo);
- testTextBeforeOffset(4, BOUNDARY_WORD_END, "", 0, 0,
- "input", kTodo, kOk, kTodo,
- "div", kTodo, kOk, kTodo,
- "editable", kTodo, kOk, kTodo,
- "textarea", kTodo, kOk, kTodo);
- testTextBeforeOffset(5, BOUNDARY_WORD_END, "", 0, 0,
- "input", kTodo, kOk, kTodo,
- "div", kTodo, kOk, kTodo,
- "editable", kTodo, kOk, kTodo,
- "textarea", kTodo, kOk, kTodo);
- testTextBeforeOffset(6, BOUNDARY_WORD_END, "Brave", 0, 5,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(7, BOUNDARY_WORD_END, "Brave", 0, 5,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(8, BOUNDARY_WORD_END, "Brave", 0, 5,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(9, BOUNDARY_WORD_END, "Brave", 0, 5,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(10, BOUNDARY_WORD_END, " Sir", 5, 9,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(11, BOUNDARY_WORD_END, " Sir", 5, 9,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(15, BOUNDARY_WORD_END, " Sir", 5, 9,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(16, BOUNDARY_WORD_END, " Sir", 5, 9,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(17, BOUNDARY_WORD_END, " Robin", 9, 16,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(18, BOUNDARY_WORD_END, " Robin", 9, 16,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(19, BOUNDARY_WORD_END, " Robin", 9, 16,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(21, BOUNDARY_WORD_END, " Robin", 9, 16,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
- testTextBeforeOffset(22, BOUNDARY_WORD_END, " Robin", 9, 16,
- "input", kTodo, kTodo, kTodo,
- "div", kTodo, kTodo, kTodo,
- "editable", kTodo, kTodo, kTodo,
- "textarea", kTodo, kTodo, kTodo);
+ testTextBeforeOffset(0, BOUNDARY_WORD_END, "", 0, 0, IDs);
+ testTextBeforeOffset(1, BOUNDARY_WORD_END, "", 0, 0, IDs);
+ testTextBeforeOffset(4, BOUNDARY_WORD_END, "", 0, 0, IDs);
+ testTextBeforeOffset(5, BOUNDARY_WORD_END, "", 0, 0, IDs);
+ testTextBeforeOffset(6, BOUNDARY_WORD_END, "Brave", 0, 5, IDs);
+ testTextBeforeOffset(7, BOUNDARY_WORD_END, "Brave", 0, 5, IDs);
+ testTextBeforeOffset(8, BOUNDARY_WORD_END, "Brave", 0, 5, IDs);
+ testTextBeforeOffset(9, BOUNDARY_WORD_END, "Brave", 0, 5, IDs);
+ testTextBeforeOffset(10, BOUNDARY_WORD_END, " Sir", 5, 9, IDs);
+ testTextBeforeOffset(11, BOUNDARY_WORD_END, " Sir", 5, 9, IDs);
+ testTextBeforeOffset(15, BOUNDARY_WORD_END, " Sir", 5, 9, IDs);
+ testTextBeforeOffset(16, BOUNDARY_WORD_END, " Sir", 5, 9, IDs);
+ testTextBeforeOffset(17, BOUNDARY_WORD_END, " Robin", 9, 16, IDs);
+ testTextBeforeOffset(18, BOUNDARY_WORD_END, " Robin", 9, 16, IDs);
+ testTextBeforeOffset(19, BOUNDARY_WORD_END, " Robin", 9, 16, IDs);
+ testTextBeforeOffset(21, BOUNDARY_WORD_END, " Robin", 9, 16, IDs);
+ testTextBeforeOffset(22, BOUNDARY_WORD_END, " Robin", 9, 16, IDs);
////////////////////////////////////////////////////////////////////////
// getTextAtOffset
// BOUNDARY_CHAR
testTextAtOffset(0, BOUNDARY_CHAR, "B", 0, 1,
"input", kOk, kOk, kOk,
"div", kOk, kOk, kOk,
@@ -487,17 +363,29 @@
SimpleTest.waitForExplicitFinish();
addA11yLoadEvent(doTest);
</script>
</head>
<body>
<a target="_blank"
title="getText... methods tests on string with whitespaces for plain text containers"
- href="https://bugzilla.mozilla.org/show_bug.cgi?id=610568">Mozilla Bug 610568</a>
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=610568">
+ Bug 610568
+ </a>
+ <a target="_blank"
+ title="getTextAtOffset for word boundaries: beginning of a new life"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=853340">
+ Bug 853340
+ </a>
+ <a target="_blank"
+ title="getTextBeforeOffset for word boundaries: evolving"
+ href="https://bugzilla.mozilla.org/show_bug.cgi?id=855732">
+ Bug 855732
+ </a>
<p id="display"></p>
<div id="content" style="display: none"></div>
<pre id="test">
<input id="input" value="Brave Sir Robin ran"/>
<div id="div">Brave Sir Robin ran</div>
<div id="editable" contenteditable="true">Brave Sir Robin ran</div>
<textarea id="textarea" cols="300">Brave Sir Robin ran</textarea>
</pre>
--- a/browser/app/nsBrowserApp.cpp
+++ b/browser/app/nsBrowserApp.cpp
@@ -5,16 +5,18 @@
#include "nsXULAppAPI.h"
#include "mozilla/AppData.h"
#include "application.ini.h"
#include "nsXPCOMGlue.h"
#if defined(XP_WIN)
#include <windows.h>
#include <stdlib.h>
+#include <io.h>
+#include <fcntl.h>
#elif defined(XP_UNIX)
#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#endif
#ifdef XP_MACOSX
#include "MacQuirks.h"
@@ -44,17 +46,20 @@
#include "mozilla/Telemetry.h"
using namespace mozilla;
#define kDesktopFolder "browser"
#define kMetroFolder "metro"
#define kMetroAppIniFilename "metroapp.ini"
+#ifdef XP_WIN
#define kMetroTestFile "tests.ini"
+const char* kMetroConsoleIdParam = "testconsoleid=";
+#endif
static void Output(const char *fmt, ... )
{
va_list ap;
va_start(ap, fmt);
#ifndef XP_WIN
vfprintf(stderr, fmt, ap);
@@ -96,16 +101,52 @@ static bool IsArg(const char* arg, const
#if defined(XP_WIN) || defined(XP_OS2)
if (*arg == '/')
return !strcasecmp(++arg, s);
#endif
return false;
}
+#ifdef XP_WIN
+/*
+ * AttachToTestsConsole - Windows helper for when we are running
+ * in the immersive environment. Firefox is launched by Windows in
+ * response to a request by metrotestharness, which is launched by
+ * runtests.py. As such stdout in fx doesn't point to the right
+ * stream. This helper touches up stdout such that test output gets
+ * routed to the console the tests are run in.
+ */
+static void AttachToTestsConsole(DWORD aProcessId)
+{
+ if (!AttachConsole(aProcessId)) {
+ OutputDebugStringW(L"Could not attach to console.\n");
+ return;
+ }
+
+ HANDLE winOut = CreateFileA("CONOUT$",
+ GENERIC_READ | GENERIC_WRITE,
+ FILE_SHARE_WRITE, 0,
+ OPEN_EXISTING, 0, 0);
+ if (winOut == INVALID_HANDLE_VALUE) {
+ OutputDebugStringW(L"Could not attach to console.\n");
+ return;
+ }
+
+ // Set the c runtime handle
+ int stdOut = _open_osfhandle((intptr_t)winOut, _O_APPEND);
+ if (stdOut == -1) {
+ OutputDebugStringW(L"Could not open c-runtime handle.\n");
+ return;
+ }
+ FILE *fp = _fdopen(stdOut, "a");
+ *stdout = *fp;
+}
+#endif
+
XRE_GetFileFromPathType XRE_GetFileFromPath;
XRE_CreateAppDataType XRE_CreateAppData;
XRE_FreeAppDataType XRE_FreeAppData;
#ifdef XRE_HAS_DLL_BLOCKLIST
XRE_SetupDllBlocklistType XRE_SetupDllBlocklist;
#endif
XRE_TelemetryAccumulateType XRE_TelemetryAccumulate;
XRE_StartupTimelineRecordType XRE_StartupTimelineRecord;
@@ -309,16 +350,25 @@ static int do_main(int argc, char* argv[
char* ptr = buffer;
newArgv[0] = ptr;
while (*ptr != NULL &&
(ptr - buffer) < sizeof(buffer) &&
newArgc < ARRAYSIZE(newArgv)) {
if (isspace(*ptr)) {
*ptr = '\0';
ptr++;
+ // Check for the console id the metrotestharness passes in, we need
+ // to connect up to this so test output goes to the right place.
+ if (ptr && !strncmp(ptr, kMetroConsoleIdParam, strlen(kMetroConsoleIdParam))) {
+ DWORD processId = strtol(ptr + strlen(kMetroConsoleIdParam), nullptr, 10);
+ if (processId > 0) {
+ AttachToTestsConsole(processId);
+ }
+ continue;
+ }
newArgv[newArgc] = ptr;
newArgc++;
continue;
}
ptr++;
}
if (ptr == newArgv[newArgc-1])
newArgc--;
--- a/browser/app/profile/firefox.js
+++ b/browser/app/profile/firefox.js
@@ -1227,8 +1227,11 @@ pref("dom.identity.enabled", false);
// Override the Gecko-default value of false for Firefox.
pref("plain_text.wrap_long_lines", true);
#ifndef RELEASE_BUILD
// Enable Web Audio for Firefox Desktop in Nightly and Aurora
pref("media.webaudio.enabled", true);
#endif
+// If this turns true, Moz*Gesture events are not called stopPropagation()
+// before content.
+pref("dom.debug.propagate_gesture_events_through_content", false);
--- a/browser/base/content/browser.js
+++ b/browser/base/content/browser.js
@@ -763,17 +763,20 @@ let gGestureSupport = {
* Dispatch events based on the type of mouse gesture event. For now, make
* sure to stop propagation of every gesture event so that web content cannot
* receive gesture events.
*
* @param aEvent
* The gesture event to handle
*/
handleEvent: function GS_handleEvent(aEvent) {
- aEvent.stopPropagation();
+ if (!Services.prefs.getBoolPref(
+ "dom.debug.propagate_gesture_events_through_content")) {
+ aEvent.stopPropagation();
+ }
// Create a preference object with some defaults
let def = function(aThreshold, aLatched)
({ threshold: aThreshold, latched: !!aLatched });
switch (aEvent.type) {
case "MozSwipeGesture":
aEvent.preventDefault();
@@ -4208,42 +4211,16 @@ var XULBrowserWindow = {
if (tooltipWindow == aWebProgress.DOMWindow) {
pageTooltip.hidePopup();
break;
}
}
}
}
- // This code here does not compare uris exactly when determining
- // whether or not the message should be hidden since the message
- // may be prematurely hidden when an install is invoked by a click
- // on a link that looks like this:
- //
- // <a href="#" onclick="return install();">Install Foo</a>
- //
- // - which fires a onLocationChange message to uri + '#'...
- var selectedBrowser = gBrowser.selectedBrowser;
- if (selectedBrowser.lastURI) {
- let oldSpec = selectedBrowser.lastURI.spec;
- let oldIndexOfHash = oldSpec.indexOf("#");
- if (oldIndexOfHash != -1)
- oldSpec = oldSpec.substr(0, oldIndexOfHash);
- let newSpec = location;
- let newIndexOfHash = newSpec.indexOf("#");
- if (newIndexOfHash != -1)
- newSpec = newSpec.substr(0, newIndexOfHash);
- if (newSpec != oldSpec) {
- // Remove all the notifications, except for those which want to
- // persist across the first location change.
- let nBox = gBrowser.getNotificationBox(selectedBrowser);
- nBox.removeTransientNotifications();
- }
- }
-
// Disable menu entries for images, enable otherwise
if (content.document && mimeTypeIsTextBased(content.document.contentType))
this.isImage.removeAttribute('disabled');
else
this.isImage.setAttribute('disabled', 'true');
this.hideOverLinkImmediately = true;
this.setOverLink("", null);
@@ -4686,36 +4663,36 @@ var TabsProgressListener = {
// We also want to make changes to page UI for unprivileged about pages.
BrowserOnAboutPageLoad(doc);
}
},
onLocationChange: function (aBrowser, aWebProgress, aRequest, aLocationURI,
aFlags) {
- // Filter out sub-frame loads and location changes caused by anchor
- // navigation or history.push/pop/replaceState.
- if (aBrowser.contentWindow == aWebProgress.DOMWindow &&
- !(aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)) {
+ // Filter out location changes caused by anchor navigation
+ // or history.push/pop/replaceState.
+ if (aFlags & Ci.nsIWebProgressListener.LOCATION_CHANGE_SAME_DOCUMENT)
+ return;
+
+ // Only need to call locationChange if the PopupNotifications object
+ // for this window has already been initialized (i.e. its getter no
+ // longer exists)
+ if (!Object.getOwnPropertyDescriptor(window, "PopupNotifications").get)
+ PopupNotifications.locationChange(aBrowser);
+
+ gBrowser.getNotificationBox(aBrowser).removeTransientNotifications();
+
+ // Filter out location changes in sub documents.
+ if (aBrowser.contentWindow == aWebProgress.DOMWindow) {
// Initialize the click-to-play state.
aBrowser._clickToPlayPluginsActivated = new Map();
aBrowser._clickToPlayAllPluginsActivated = false;
aBrowser._pluginScriptedState = gPluginHandler.PLUGIN_SCRIPTED_STATE_NONE;
- // Only need to call locationChange if the PopupNotifications object
- // for this window has already been initialized (i.e. its getter no
- // longer exists)
- if (!Object.getOwnPropertyDescriptor(window, "PopupNotifications").get)
- PopupNotifications.locationChange(aBrowser);
-
- // Only handle background browsers as long as the selected browser is
- // handled in XULBrowserWindow.onLocationChange (bug 839516).
- if (aBrowser != gBrowser.selectedBrowser)
- gBrowser.getNotificationBox(aBrowser).removeTransientNotifications();
-
FullZoom.onLocationChange(aLocationURI, false, aBrowser);
}
},
onRefreshAttempted: function (aBrowser, aWebProgress, aURI, aDelay, aSameURI) {
if (gPrefService.getBoolPref("accessibility.blockautorefresh")) {
let brandBundle = document.getElementById("bundle_brand");
let brandShortName = brandBundle.getString("brandShortName");
--- a/browser/base/content/tabbrowser.xml
+++ b/browser/base/content/tabbrowser.xml
@@ -2921,18 +2921,18 @@
# right of the newtab button.
<children includes="tab"/>
# This is to ensure anything extensions put here will go before the newtab
# button, necessary due to the previous hack.
<children/>
<xul:toolbarbutton class="tabs-newtab-button"
command="cmd_newNavigatorTab"
onclick="checkForMiddleClick(this, event);"
- onmouseenter="document.getBindingParent(this)._enterNewTab();"
- onmouseleave="document.getBindingParent(this)._leaveNewTab();"
+ onmouseover="document.getBindingParent(this)._enterNewTab();"
+ onmouseout="document.getBindingParent(this)._leaveNewTab();"
tooltiptext="&newTabButton.tooltip;"/>
<xul:spacer class="closing-tabs-spacer" anonid="closing-tabs-spacer"
style="width: 0;"/>
</xul:arrowscrollbox>
</content>
<implementation implements="nsIDOMEventListener">
<constructor>
@@ -4263,27 +4263,21 @@
<field name="mOverCloseButton">false</field>
<field name="mCorrespondingMenuitem">null</field>
<field name="closing">false</field>
<field name="lastAccessed">0</field>
</implementation>
<handlers>
- <handler event="mouseover">
- var anonid = event.originalTarget.getAttribute("anonid");
+ <handler event="mouseover"><![CDATA[
+ let anonid = event.originalTarget.getAttribute("anonid");
if (anonid == "close-button")
this.mOverCloseButton = true;
- </handler>
- <handler event="mouseout">
- var anonid = event.originalTarget.getAttribute("anonid");
- if (anonid == "close-button")
- this.mOverCloseButton = false;
- </handler>
- <handler event="mouseenter" phase="target"><![CDATA[
+
let tab = event.target;
if (tab.selected)
return;
let tabContainer = this.parentNode;
let visibleTabs = tabContainer.tabbrowser.visibleTabs;
let tabIndex = visibleTabs.indexOf(tab);
if (tabIndex == 0) {
@@ -4301,17 +4295,21 @@
} else {
let candidate = visibleTabs[tabIndex + 1];
if (!candidate.selected) {
tabContainer._afterHoveredTab = candidate;
candidate.setAttribute("afterhovered", "true");
}
}
]]></handler>
- <handler event="mouseleave" phase="target"><![CDATA[
+ <handler event="mouseout"><![CDATA[
+ let anonid = event.originalTarget.getAttribute("anonid");
+ if (anonid == "close-button")
+ this.mOverCloseButton = false;
+
let tabContainer = this.parentNode;
if (tabContainer._beforeHoveredTab) {
tabContainer._beforeHoveredTab.removeAttribute("beforehovered");
tabContainer._beforeHoveredTab = null;
}
if (tabContainer._afterHoveredTab) {
tabContainer._afterHoveredTab.removeAttribute("afterhovered");
tabContainer._afterHoveredTab = null;
--- a/browser/base/content/test/browser_popupNotification.js
+++ b/browser/base/content/test/browser_popupNotification.js
@@ -720,17 +720,36 @@ var tests = [
},
onHidden: function (popup) {
ok(this.notifyObj.mainActionClicked, "mainAction was clicked after the delay");
ok(!this.notifyObj.dismissalCallbackTriggered, "dismissal callback was not triggered");
PopupNotifications.buttonDelay = PREF_SECURITY_DELAY_INITIAL;
},
},
- { // Test #25 - location change in background tab removes notification
+ { // Test #25 - reload removes notification
+ run: function () {
+ loadURI("http://example.com/", function() {
+ let notifyObj = new basicNotification();
+ notifyObj.options.eventCallback = function (eventName) {
+ if (eventName == "removed") {
+ ok(true, "Notification removed in background tab after reloading");
+ executeSoon(function () {
+ goNext();
+ });
+ }
+ };
+ showNotification(notifyObj);
+ executeSoon(function () {
+ gBrowser.selectedBrowser.reload();
+ });
+ });
+ }
+ },
+ { // Test #26 - location change in background tab removes notification
run: function () {
let oldSelectedTab = gBrowser.selectedTab;
let newTab = gBrowser.addTab("about:blank");
gBrowser.selectedTab = newTab;
loadURI("http://example.com/", function() {
gBrowser.selectedTab = oldSelectedTab;
let browser = gBrowser.getBrowserForTab(newTab);
@@ -748,17 +767,17 @@ var tests = [
};
showNotification(notifyObj);
executeSoon(function () {
browser.reload();
});
});
}
},
- { // Test #26 - Popup notification anchor shouldn't disappear when a notification with the same ID is re-added in a background tab
+ { // Test #27 - Popup notification anchor shouldn't disappear when a notification with the same ID is re-added in a background tab
run: function () {
loadURI("http://example.com/", function () {
let originalTab = gBrowser.selectedTab;
let bgTab = gBrowser.addTab("about:blank");
gBrowser.selectedTab = bgTab;
loadURI("http://example.com/", function () {
let anchor = document.createElement("box");
anchor.id = "test26-anchor";
@@ -785,16 +804,34 @@ var tests = [
is(anchor.getAttribute("showing"), "true", "anchor still showing");
fgNotification.remove();
gBrowser.removeTab(bgTab);
goNext();
});
});
}
+ },
+ { // Test #28 - location change in embedded frame removes notification
+ run: function () {
+ loadURI("data:text/html,<iframe id='iframe' src='http://example.com/'>", function () {
+ let notifyObj = new basicNotification();
+ notifyObj.options.eventCallback = function (eventName) {
+ if (eventName == "removed") {
+ ok(true, "Notification removed in background tab after reloading");
+ executeSoon(goNext);
+ }
+ };
+ showNotification(notifyObj);
+ executeSoon(function () {
+ content.document.getElementById("iframe")
+ .setAttribute("src", "http://example.org/");
+ });
+ });
+ }
}
];
function showNotification(notifyObj) {
return PopupNotifications.show(notifyObj.browser,
notifyObj.id,
notifyObj.message,
notifyObj.anchorID,
@@ -872,17 +909,17 @@ function triggerSecondaryCommand(popup,
// One down event to open the popup
EventUtils.synthesizeKey("VK_DOWN", { altKey: !navigator.platform.contains("Mac") });
}
function loadURI(uri, callback) {
if (callback) {
gBrowser.addEventListener("load", function() {
// Ignore the about:blank load
- if (gBrowser.currentURI.spec != uri)
+ if (gBrowser.currentURI.spec == "about:blank")
return;
gBrowser.removeEventListener("load", arguments.callee, true);
callback();
}, true);
}
gBrowser.loadURI(uri);
--- a/browser/extensions/pdfjs/README.mozilla
+++ b/browser/extensions/pdfjs/README.mozilla
@@ -1,4 +1,4 @@
This is the pdf.js project output, https://github.com/mozilla/pdf.js
-Current extension version is: 0.7.390
+Current extension version is: 0.7.423
--- a/browser/extensions/pdfjs/components/PdfStreamConverter.js
+++ b/browser/extensions/pdfjs/components/PdfStreamConverter.js
@@ -371,18 +371,19 @@ ChromeActions.prototype = {
supportsIntegratedFind: function() {
// Integrated find is only supported when we're not in a frame and when the
// new find events code exists.
return this.domWindow.frameElement === null &&
getChromeWindow(this.domWindow).gFindBar &&
'updateControlState' in getChromeWindow(this.domWindow).gFindBar;
},
supportsDocumentFonts: function() {
- var pref = getIntPref('browser.display.use_document_fonts', 1);
- return !!pref;
+ var prefBrowser = getIntPref('browser.display.use_document_fonts', 1);
+ var prefGfx = getBoolPref('gfx.downloadable_fonts.enabled', true);
+ return (!!prefBrowser && prefGfx);
},
fallback: function(url, sendResponse) {
var self = this;
var domWindow = this.domWindow;
var strings = getLocalizedStrings('chrome.properties');
var message = getLocalizedString(strings, 'unsupported_feature');
var notificationBox = null;
--- a/browser/extensions/pdfjs/content/PdfJs.jsm
+++ b/browser/extensions/pdfjs/content/PdfJs.jsm
@@ -187,16 +187,17 @@ let PdfJs = {
get enabled() {
var disabled = getBoolPref(PREF_DISABLED, true);
if (disabled)
return false;
var handlerInfo = Svc.mime.
getFromTypeAndExtension('application/pdf', 'pdf');
return handlerInfo.alwaysAskBeforeHandling == false &&
+ handlerInfo.plugin == null &&
handlerInfo.preferredAction == Ci.nsIHandlerInfo.handleInternally;
},
_ensureRegistered: function _ensureRegistered() {
if (this._registered)
return;
this._pdfStreamConverterFactory = new Factory();
--- a/browser/extensions/pdfjs/content/build/pdf.js
+++ b/browser/extensions/pdfjs/content/build/pdf.js
@@ -11,18 +11,18 @@
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
var PDFJS = {};
-PDFJS.version = '0.7.390';
-PDFJS.build = '921f321';
+PDFJS.version = '0.7.423';
+PDFJS.build = 'aa22bef';
(function pdfjsWrapper() {
// Use strict in our context only - users might not want it
'use strict';
/* -*- Mode: Java; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set shiftwidth=2 tabstop=2 autoindent cindent expandtab: */
/* Copyright 2012 Mozilla Foundation
@@ -952,23 +952,29 @@ var Util = PDFJS.Util = (function UtilCl
Util.sign = function Util_sign(num) {
return num < 0 ? -1 : 1;
};
return Util;
})();
var PageViewport = PDFJS.PageViewport = (function PageViewportClosure() {
- function PageViewport(viewBox, scale, rotate, offsetX, offsetY) {
+ function PageViewport(viewBox, scale, rotation, offsetX, offsetY) {
+ this.viewBox = viewBox;
+ this.scale = scale;
+ this.rotation = rotation;
+ this.offsetX = offsetX;
+ this.offsetY = offsetY;
+
// creating transform to convert pdf coordinate system to the normal
// canvas like coordinates taking in account scale and rotation
var centerX = (viewBox[2] + viewBox[0]) / 2;
var centerY = (viewBox[3] + viewBox[1]) / 2;
var rotateA, rotateB, rotateC, rotateD;
- switch (rotate % 360) {
+ switch (rotation % 360) {
case -180:
case 180:
rotateA = -1; rotateB = 0; rotateC = 0; rotateD = 1;
break;
case -270:
case 90:
rotateA = 0; rotateB = 1; rotateC = 1; rotateD = 0;
break;
@@ -1002,23 +1008,28 @@ var PageViewport = PDFJS.PageViewport =
rotateA * scale,
rotateB * scale,
rotateC * scale,
rotateD * scale,
offsetCanvasX - rotateA * scale * centerX - rotateC * scale * centerY,
offsetCanvasY - rotateB * scale * centerX - rotateD * scale * centerY
];
- this.offsetX = offsetX;
- this.offsetY = offsetY;
this.width = width;
this.height = height;
this.fontScale = scale;
}
PageViewport.prototype = {
+ clone: function PageViewPort_clone(args) {
+ args = args || {};
+ var scale = 'scale' in args ? args.scale : this.scale;
+ var rotation = 'rotation' in args ? args.rotation : this.rotation;
+ return new PageViewport(this.viewBox.slice(), scale, rotation,
+ this.offsetX, this.offsetY);
+ },
convertToViewportPoint: function PageViewport_convertToViewportPoint(x, y) {
return Util.applyTransform([x, y], this.transform);
},
convertToViewportRectangle:
function PageViewport_convertToViewportRectangle(rect) {
var tl = Util.applyTransform([rect[0], rect[1]], this.transform);
var br = Util.applyTransform([rect[2], rect[3]], this.transform);
return [tl[0], tl[1], br[0], br[1]];
@@ -1720,21 +1731,21 @@ var PDFPageProxy = (function PDFPageProx
/**
* For internal use only.
*/
display: function PDFPageProxy_display(gfx, viewport, callback,
continueCallback) {
var stats = this.stats;
stats.time('Rendering');
- gfx.beginDrawing(viewport);
+ var operatorList = this.operatorList;
+ gfx.beginDrawing(viewport, operatorList.transparency);
var startIdx = 0;
- var length = this.operatorList.fnArray.length;
- var operatorList = this.operatorList;
+ var length = operatorList.fnArray.length;
var stepper = null;
if (PDFJS.pdfBug && 'StepperManager' in globalScope &&
globalScope['StepperManager'].enabled) {
stepper = globalScope['StepperManager'].create(this.pageNumber - 1);
stepper.init(operatorList);
stepper.nextBreakPoint = stepper.getNextBreakPoint();
}
@@ -2462,17 +2473,34 @@ var CanvasGraphics = (function CanvasGra
'paintImageXObject': true,
'paintInlineImageXObject': true,
'paintInlineImageXObjectGroup': true,
'paintImageMaskXObject': true,
'paintImageMaskXObjectGroup': true,
'shadingFill': true
},
- beginDrawing: function CanvasGraphics_beginDrawing(viewport) {
+ beginDrawing: function CanvasGraphics_beginDrawing(viewport, transparency) {
+ // For pdfs that use blend modes we have to clear the canvas else certain
+ // blend modes can look wrong since we'd be blending with a white
+ // backdrop. The problem with a transparent backdrop though is we then
+ // don't get sub pixel anti aliasing on text, so we fill with white if
+ // we can.
+ var width = this.ctx.canvas.width;
+ var height = this.ctx.canvas.height;
+ if (transparency) {
+ this.ctx.clearRect(0, 0, width, height);
+ } else {
+ this.ctx.mozOpaque = true;
+ this.ctx.save();
+ this.ctx.fillStyle = 'rgb(255, 255, 255)';
+ this.ctx.fillRect(0, 0, width, height);
+ this.ctx.restore();
+ }
+
var transform = viewport.transform;
this.ctx.save();
this.ctx.transform.apply(this.ctx, transform);
if (this.textLayer) {
this.textLayer.beginLayout();
}
if (this.imageLayer) {
@@ -12625,18 +12653,45 @@ var ColorSpace = (function ColorSpaceClo
* Creates the output buffer and converts the specified number of the color
* values to the RGB colors, similar to the getRgbBuffer.
*/
createRgbBuffer: function ColorSpace_createRgbBuffer(src, srcOffset,
count, bits) {
if (this.isPassthrough(bits)) {
return src.subarray(srcOffset);
}
- var destLength = this.getOutputLength(count * this.numComps);
- var dest = new Uint8Array(destLength);
+ var dest = new Uint8Array(count * 3);
+ var numComponentColors = 1 << bits;
+ // Optimization: create a color map when there is just one component and
+ // we are converting more colors than the size of the color map. We
+ // don't build the map if the colorspace is gray or rgb since those
+ // methods are faster than building a map. This mainly offers big speed
+ // ups for indexed and alternate colorspaces.
+ if (this.numComps === 1 && count > numComponentColors &&
+ this.name !== 'DeviceGray' && this.name !== 'DeviceRGB') {
+ // TODO it may be worth while to cache the color map. While running
+ // testing I never hit a cache so I will leave that out for now (perhaps
+ // we are reparsing colorspaces too much?).
+ var allColors = bits <= 8 ? new Uint8Array(numComponentColors) :
+ new Uint16Array(numComponentColors);
+ for (var i = 0; i < numComponentColors; i++) {
+ allColors[i] = i;
+ }
+ var colorMap = new Uint8Array(numComponentColors * 3);
+ this.getRgbBuffer(allColors, 0, numComponentColors, colorMap, 0, bits);
+
+ var destOffset = 0;
+ for (var i = 0; i < count; ++i) {
+ var key = src[srcOffset++] * 3;
+ dest[destOffset++] = colorMap[key];
+ dest[destOffset++] = colorMap[key + 1];
+ dest[destOffset++] = colorMap[key + 2];
+ }
+ return dest;
+ }
this.getRgbBuffer(src, srcOffset, count, dest, 0, bits);
return dest;
}
};
ColorSpace.parse = function ColorSpace_parse(cs, xref, res) {
var IR = ColorSpace.parseToIR(cs, xref, res);
if (IR instanceof AlternateCS)
@@ -13029,17 +13084,17 @@ var DeviceRgbCS = (function DeviceRgbCSC
var length = count * 3;
if (bits == 8) {
dest.set(src.subarray(srcOffset, srcOffset + length), destOffset);
return;
}
var scale = 255 / ((1 << bits) - 1);
var j = srcOffset, q = destOffset;
for (var i = 0; i < length; ++i) {
- dest[q++] = (scale * input[j++]) | 0;
+ dest[q++] = (scale * src[j++]) | 0;
}
},
getOutputLength: function DeviceRgbCS_getOutputLength(inputLength) {
return inputLength;
},
isPassthrough: function DeviceRgbCS_isPassthrough(bits) {
return bits == 8;
},
@@ -14904,18 +14959,21 @@ var PartialEvaluator = (function Partial
fn = 'paintImageXObject';
PDFImage.buildImage(function(imageObj) {
var imgData = imageObj.getImageData();
handler.send('obj', [objId, pageIndex, 'Image', imgData]);
}, handler, xref, resources, image, inline);
}
- if (!queue)
- queue = {};
+ if (!queue) {
+ queue = {
+ transparency: false
+ };
+ }
if (!queue.argsArray) {
queue.argsArray = [];
}
if (!queue.fnArray) {
queue.fnArray = [];
}
@@ -15075,26 +15133,31 @@ var PartialEvaluator = (function Partial
case 'LC':
case 'LJ':
case 'ML':
case 'D':
case 'RI':
case 'FL':
case 'CA':
case 'ca':
- case 'BM':
gsStateObj.push([key, value]);
break;
case 'Font':
gsStateObj.push([
'Font',
handleSetFont(null, value[0]),
value[1]
]);
break;
+ case 'BM':
+ if (!isName(value) || value.name !== 'Normal') {
+ queue.transparency = true;
+ }
+ gsStateObj.push([key, value]);
+ break;
case 'SMask':
// We support the default so don't trigger the TODO.
if (!isName(value) || value.name != 'None')
TODO('graphic state operator ' + key);
break;
// Only generate info log messages for the following since
// they are unlikey to have a big impact on the rendering.
case 'OP':
@@ -18168,16 +18231,20 @@ var Font = (function FontClosure() {
case 'TrueType':
case 'CIDFontType2':
this.mimetype = 'font/opentype';
// Repair the TrueType file. It is can be damaged in the point of
// view of the sanitizer
data = this.checkAndRepair(name, file, properties);
+ if (!data) {
+ // TrueType data is not found, e.g. when the font is an OpenType font
+ warn('Font is not a TrueType font');
+ }
break;
default:
warn('Font ' + type + ' is not supported');
break;
}
this.data = data;
@@ -19514,16 +19581,18 @@ var Font = (function FontClosure() {
else if (table.tag == 'glyf')
glyf = table;
else if (table.tag == 'fpgm')
fpgm = table;
else if (table.tag == 'prep')
prep = table;
else if (table.tag == 'cvt ')
cvt = table;
+ else if (table.tag == 'CFF ')
+ return null; // XXX: OpenType font is found, stopping
else // skipping table if it's not a required or optional table
continue;
}
tables.push(table);
}
var numTables = tables.length + requiredTables.length;
@@ -21147,23 +21216,28 @@ var Type1Font = function Type1Font(name,
subrs, properties);
this.seacs = this.getSeacs(data.charstrings);
};
Type1Font.prototype = {
getOrderedCharStrings: function Type1Font_getOrderedCharStrings(glyphs,
properties) {
var charstrings = [];
+ var usedUnicodes = [];
var i, length, glyphName;
var unusedUnicode = CMAP_GLYPH_OFFSET;
for (i = 0, length = glyphs.length; i < length; i++) {
var item = glyphs[i];
var glyphName = item.glyph;
var unicode = glyphName in GlyphsUnicode ?
GlyphsUnicode[glyphName] : unusedUnicode++;
+ while (usedUnicodes[unicode]) {
+ unicode = unusedUnicode++;
+ }
+ usedUnicodes[unicode] = true;
charstrings.push({
glyph: glyphName,
unicode: unicode,
gid: i,
charstring: item.data,
width: item.width,
lsb: item.lsb
});
--- a/browser/extensions/pdfjs/content/web/viewer.js
+++ b/browser/extensions/pdfjs/content/web/viewer.js
@@ -767,16 +767,20 @@ var PDFView = {
PDFFindController.initialize();
this.initialized = true;
container.addEventListener('scroll', function() {
self.lastScroll = Date.now();
}, false);
},
+ getPage: function pdfViewGetPage(n) {
+ return this.pdfDocument.getPage(n);
+ },
+
// Helper function to keep track whether a div was scrolled up or down and
// then call a callback.
watchScroll: function pdfViewWatchScroll(viewAreaElement, state, callback) {
state.down = true;
state.lastY = viewAreaElement.scrollTop;
viewAreaElement.addEventListener('scroll', function webViewerScroll(evt) {
var currentY = viewAreaElement.scrollTop;
var lastY = state.lastY;
@@ -1251,47 +1255,94 @@ var PDFView = {
var pagesCount = pdfDocument.numPages;
var id = pdfDocument.fingerprint;
document.getElementById('numPages').textContent =
mozL10n.get('page_of', {pageCount: pagesCount}, 'of {{pageCount}}');
document.getElementById('pageNumber').max = pagesCount;
PDFView.documentFingerprint = id;
var store = PDFView.store = new Settings(id);
- var storePromise = store.initializedPromise;
this.pageRotation = 0;
var pages = this.pages = [];
this.pageText = [];
this.startedTextExtraction = false;
- var pagesRefMap = {};
+ var pagesRefMap = this.pagesRefMap = {};
var thumbnails = this.thumbnails = [];
- var pagePromises = [];
- for (var i = 1; i <= pagesCount; i++)
- pagePromises.push(pdfDocument.getPage(i));
+
+ var pagesPromise = new PDFJS.Promise();
var self = this;
- var pagesPromise = PDFJS.Promise.all(pagePromises);
- pagesPromise.then(function(promisedPages) {
- for (var i = 1; i <= pagesCount; i++) {
- var page = promisedPages[i - 1];
- var pageView = new PageView(container, page, i, scale,
- page.stats, self.navigateTo.bind(self));
- var thumbnailView = new ThumbnailView(thumbsView, page, i);
+
+ var firstPagePromise = pdfDocument.getPage(1);
+
+ // Fetch a single page so we can get a viewport that will be the default
+ // viewport for all pages
+ firstPagePromise.then(function(pdfPage) {
+ var viewport = pdfPage.getViewport(scale || 1.0);
+ var pagePromises = [];
+ for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
+ var viewportClone = viewport.clone();
+ var pageView = new PageView(container, pageNum, scale,
+ self.navigateTo.bind(self),
+ viewportClone);
+ var thumbnailView = new ThumbnailView(thumbsView, pageNum,
+ viewportClone);
bindOnAfterDraw(pageView, thumbnailView);
-
pages.push(pageView);
thumbnails.push(thumbnailView);
- var pageRef = page.ref;
- pagesRefMap[pageRef.num + ' ' + pageRef.gen + ' R'] = i;
}
- self.pagesRefMap = pagesRefMap;
-
- // Wait to do this here so all the canvases are setup.
+ var event = document.createEvent('CustomEvent');
+ event.initCustomEvent('documentload', true, true, {});
+ window.dispatchEvent(event);
+
+ for (var pageNum = 1; pageNum <= pagesCount; ++pageNum) {
+ var pagePromise = pdfDocument.getPage(pageNum);
+ pagePromise.then(function(pdfPage) {
+ var pageNum = pdfPage.pageNumber;
+ var pageView = pages[pageNum - 1];
+ if (!pageView.pdfPage) {
+ // The pdfPage might already be set if we've already entered
+ // pageView.draw()
+ pageView.setPdfPage(pdfPage);
+ }
+ var thumbnailView = thumbnails[pageNum - 1];
+ if (!thumbnailView.pdfPage) {
+ thumbnailView.setPdfPage(pdfPage);
+ }
+
+ var pageRef = pdfPage.ref;
+ var refStr = pageRef.num + ' ' + pageRef.gen + ' R';
+ pagesRefMap[refStr] = pdfPage.pageNumber;
+ });
+ pagePromises.push(pagePromise);
+ }
+
+ PDFJS.Promise.all(pagePromises).then(function(pages) {
+ pagesPromise.resolve(pages);
+ });
+ });
+
+ var storePromise = store.initializedPromise;
+ PDFJS.Promise.all([firstPagePromise, storePromise]).then(function() {
+ var storedHash = null;
+ if (store.get('exists', false)) {
+ var pageNum = store.get('page', '1');
+ var zoom = store.get('zoom', PDFView.currentScale);
+ var left = store.get('scrollLeft', '0');
+ var top = store.get('scrollTop', '0');
+
+ storedHash = 'page=' + pageNum + '&zoom=' + zoom + ',' +
+ left + ',' + top;
+ }
+ self.setInitialView(storedHash, scale);
+ });
+
+ pagesPromise.then(function() {
if (PDFView.supportsPrinting) {
pdfDocument.getJavaScript().then(function(javaScript) {
if (javaScript.length) {
console.warn('Warning: JavaScript is not supported');
PDFView.fallback();
}
// Hack to support auto printing.
var regex = /\bprint\s*\(/g;
@@ -1308,36 +1359,24 @@ var PDFView = {
}
});
var destinationsPromise = pdfDocument.getDestinations();
destinationsPromise.then(function(destinations) {
self.destinations = destinations;
});
- // outline and initial view depends on destinations and pagesRefMap
- var promises = [pagesPromise, destinationsPromise, storePromise,
+ // outline depends on destinations and pagesRefMap
+ var promises = [pagesPromise, destinationsPromise,
PDFView.animationStartedPromise];
PDFJS.Promise.all(promises).then(function() {
pdfDocument.getOutline().then(function(outline) {
self.outline = new DocumentOutlineView(outline);
});
- var storedHash = null;
- if (store.get('exists', false)) {
- var page = store.get('page', '1');
- var zoom = store.get('zoom', PDFView.currentScale);
- var left = store.get('scrollLeft', '0');
- var top = store.get('scrollTop', '0');
-
- storedHash = 'page=' + page + '&zoom=' + zoom + ',' + left + ',' + top;
- }
-
- self.setInitialView(storedHash, scale);
-
// Make all navigation keys work on document load,
// unless the viewer is embedded in another page.
if (window.parent.location === window.location) {
PDFView.container.focus();
}
});
pdfDocument.getMetadata().then(function(data) {
@@ -1644,16 +1683,35 @@ var PDFView = {
beforePrint: function pdfViewSetupBeforePrint() {
if (!this.supportsPrinting) {
var printMessage = mozL10n.get('printing_not_supported', null,
'Warning: Printing is not fully supported by this browser.');
this.error(printMessage);
return;
}
+
+ var alertNotReady = false;
+ if (!this.pages.length) {
+ alertNotReady = true;
+ } else {
+ for (var i = 0, ii = this.pages.length; i < ii; ++i) {
+ if (!this.pages[i].pdfPage) {
+ alertNotReady = true;
+ break;
+ }
+ }
+ }
+ if (alertNotReady) {
+ var notReadyMessage = mozL10n.get('printing_not_ready', null,
+ 'Warning: The PDF is not fully loaded for printing.');
+ window.alert(notReadyMessage);
+ return;
+ }
+
var body = document.querySelector('body');
body.setAttribute('data-mozPrintCallback', true);
for (var i = 0, ii = this.pages.length; i < ii; ++i) {
this.pages[i].beforePrint();
}
},
afterPrint: function pdfViewSetupAfterPrint() {
@@ -1738,25 +1796,28 @@ var PDFView = {
for (var i = 0, l = this.pages.length; i < l; i++) {
var page = this.pages[i];
page.update(page.scale, this.pageRotation);
}
for (var i = 0, l = this.thumbnails.length; i < l; i++) {
var thumb = this.thumbnails[i];
- thumb.updateRotation(this.pageRotation);
+ thumb.update(this.pageRotation);
}
- var currentPage = this.pages[this.page - 1];
-
this.parseScale(this.currentScaleValue, true);
this.renderHighestPriority();
+ var currentPage = this.pages[this.page - 1];
+ if (!currentPage) {
+ return;
+ }
+
// Wait for fullscreen to take effect
setTimeout(function() {
currentPage.scrollIntoView();
}, 0);
},
/**
* This function flips the page in presentation mode if the user scrolls up
@@ -1819,24 +1880,24 @@ var PDFView = {
* @this {PDFView}
*/
clearMouseScrollState: function pdfViewClearMouseScrollState() {
this.mouseScrollTimeStamp = 0;
this.mouseScrollDelta = 0;
}
};
-var PageView = function pageView(container, pdfPage, id, scale,
- stats, navigateTo) {
+var PageView = function pageView(container, id, scale,
+ navigateTo, defaultViewport) {
this.id = id;
- this.pdfPage = pdfPage;
this.rotation = 0;
this.scale = scale || 1.0;
- this.viewport = this.pdfPage.getViewport(this.scale, this.pdfPage.rotate);
+ this.viewport = defaultViewport;
+ this.pdfPageRotate = defaultViewport.rotate;
this.renderingState = RenderingStates.INITIAL;
this.resume = null;
this.textContent = null;
this.textLayer = null;
var anchor = document.createElement('a');
@@ -1846,37 +1907,49 @@ var PageView = function pageView(contain
div.id = 'pageContainer' + this.id;
div.className = 'page';
div.style.width = Math.floor(this.viewport.width) + 'px';
div.style.height = Math.floor(this.viewport.height) + 'px';
container.appendChild(anchor);
container.appendChild(div);
+ this.setPdfPage = function pageViewSetPdfPage(pdfPage) {
+ this.pdfPage = pdfPage;
+ this.pdfPageRotate = pdfPage.rotate;
+ this.viewport = pdfPage.getViewport(this.scale);
+ this.stats = pdfPage.stats;
+ this.update();
+ };
+
this.destroy = function pageViewDestroy() {
this.update();
- this.pdfPage.destroy();
+ if (this.pdfPage) {
+ this.pdfPage.destroy();
+ }
};
this.update = function pageViewUpdate(scale, rotation) {
this.renderingState = RenderingStates.INITIAL;
this.resume = null;
if (typeof rotation !== 'undefined') {
this.rotation = rotation;
}
this.scale = scale || this.scale;
- var totalRotation = (this.rotation + this.pdfPage.rotate) % 360;
- var viewport = this.pdfPage.getViewport(this.scale, totalRotation);
-
- this.viewport = viewport;
- div.style.width = Math.floor(viewport.width) + 'px';
- div.style.height = Math.floor(viewport.height) + 'px';
+ var totalRotation = (this.rotation + this.pdfPageRotate) % 360;
+ this.viewport = this.viewport.clone({
+ scale: this.scale,
+ rotation: totalRotation
+ });
+
+ div.style.width = Math.floor(this.viewport.width) + 'px';
+ div.style.height = Math.floor(this.viewport.height) + 'px';
while (div.hasChildNodes())
div.removeChild(div.lastChild);
div.removeAttribute('data-loaded');
delete this.canvas;
this.loadingIconDiv = document.createElement('div');
@@ -2074,54 +2147,66 @@ var PageView = function pageView(contain
this.getTextContent = function pageviewGetTextContent() {
if (!this.textContent) {
this.textContent = this.pdfPage.getTextContent();
}
return this.textContent;
};
this.draw = function pageviewDraw(callback) {
+ var pdfPage = this.pdfPage;
+
+ if (!pdfPage) {
+ var promise = PDFView.getPage(this.id);
+ promise.then(function(pdfPage) {
+ this.setPdfPage(pdfPage);
+ this.draw(callback);
+ }.bind(this));
+ return;
+ }
+
if (this.renderingState !== RenderingStates.INITIAL) {
console.error('Must be in new state before drawing');
}
this.renderingState = RenderingStates.RUNNING;
var canvas = document.createElement('canvas');
canvas.id = 'page' + this.id;
div.appendChild(canvas);
this.canvas = canvas;
+ var scale = this.scale, viewport = this.viewport;
+ var outputScale = PDFView.getOutputScale();
+ canvas.width = Math.floor(viewport.width) * outputScale.sx;
+ canvas.height = Math.floor(viewport.height) * outputScale.sy;
+
var textLayerDiv = null;
if (!PDFJS.disableTextLayer) {
textLayerDiv = document.createElement('div');
textLayerDiv.className = 'textLayer';
+ textLayerDiv.style.width = canvas.width + 'px';
+ textLayerDiv.style.height = canvas.height + 'px';
div.appendChild(textLayerDiv);
}
var textLayer = this.textLayer =
textLayerDiv ? new TextLayerBuilder(textLayerDiv, this.id - 1) : null;
- var scale = this.scale, viewport = this.viewport;
- var outputScale = PDFView.getOutputScale();
- canvas.width = Math.floor(viewport.width) * outputScale.sx;
- canvas.height = Math.floor(viewport.height) * outputScale.sy;
-
if (outputScale.scaled) {
var cssScale = 'scale(' + (1 / outputScale.sx) + ', ' +
(1 / outputScale.sy) + ')';
CustomStyle.setProp('transform' , canvas, cssScale);
CustomStyle.setProp('transformOrigin' , canvas, '0% 0%');
if (textLayerDiv) {
CustomStyle.setProp('transform' , textLayerDiv, cssScale);
CustomStyle.setProp('transformOrigin' , textLayerDiv, '0% 0%');
}
}
var ctx = canvas.getContext('2d');
- ctx.clearRect(0, 0, canvas.width, canvas.height);
// TODO(mack): use data attributes to store these
ctx._scaleX = outputScale.sx;
ctx._scaleY = outputScale.sy;
if (outputScale.scaled) {
ctx.scale(outputScale.sx, outputScale.sy);
}
// Checking if document fonts are used only once
var checkIfDocumentFontsUsed = !PDFView.pdfDocument.embeddedFontsUsed;
@@ -2154,16 +2239,23 @@ var PageView = function pageView(contain
}
self.stats = pdfPage.stats;
self.updateStats();
if (self.onAfterDraw)
self.onAfterDraw();
cache.push(self);
+
+ var event = document.createEvent('CustomEvent');
+ event.initCustomEvent('pagerender', true, true, {
+ pageNumber: pdfPage.pageNumber
+ });
+ div.dispatchEvent(event);
+
callback();
}
var renderContext = {
canvasContext: ctx,
viewport: this.viewport,
textLayer: textLayer,
continueCallback: function pdfViewcContinueCallback(cont) {
@@ -2202,16 +2294,17 @@ var PageView = function pageView(contain
}
setupAnnotations(this.pdfPage, this.viewport);
div.setAttribute('data-loaded', true);
};
this.beforePrint = function pageViewBeforePrint() {
var pdfPage = this.pdfPage;
+
var viewport = pdfPage.getViewport(1);
// Use the same hack we use for high dpi displays for printing to get better
// output until bug 811002 is fixed in FF.
var PRINT_OUTPUT_SCALE = 2;
var canvas = this.canvas = document.createElement('canvas');
canvas.width = Math.floor(viewport.width) * PRINT_OUTPUT_SCALE;
canvas.height = Math.floor(viewport.height) * PRINT_OUTPUT_SCALE;
canvas.style.width = (PRINT_OUTPUT_SCALE * viewport.width) + 'pt';
@@ -2252,163 +2345,193 @@ var PageView = function pageView(contain
else
obj.done();
self.pdfPage.destroy();
});
};
};
this.updateStats = function pageViewUpdateStats() {
+ if (!this.stats) {
+ return;
+ }
+
if (PDFJS.pdfBug && Stats.enabled) {
var stats = this.stats;
Stats.add(this.id, stats);
}
};
};
-var ThumbnailView = function thumbnailView(container, pdfPage, id) {
+var ThumbnailView = function thumbnailView(container, id, defaultViewport) {
var anchor = document.createElement('a');
anchor.href = PDFView.getAnchorUrl('#page=' + id);
anchor.title = mozL10n.get('thumb_page_title', {page: id}, 'Page {{page}}');
anchor.onclick = function stopNavigation() {
PDFView.page = id;
return false;
};
- var rotation = 0;
- var totalRotation = (rotation + pdfPage.rotate) % 360;
- var viewport = pdfPage.getViewport(1, totalRotation);
- var pageWidth = this.width = viewport.width;
- var pageHeight = this.height = viewport.height;
- var pageRatio = pageWidth / pageHeight;
+
+ this.pdfPage = undefined;
+ this.viewport = defaultViewport;
+ this.pdfPageRotate = defaultViewport.rotate;
+
+ this.rotation = 0;
+ this.pageWidth = this.viewport.width;
+ this.pageHeight = this.viewport.height;
+ this.pageRatio = this.pageWidth / this.pageHeight;
this.id = id;
- var canvasWidth = 98;
- var canvasHeight = canvasWidth / this.width * this.height;
- var scaleX = this.scaleX = (canvasWidth / pageWidth);
- var scaleY = this.scaleY = (canvasHeight / pageHeight);
+ this.canvasWidth = 98;
+ this.canvasHeight = this.canvasWidth / this.pageWidth * this.pageHeight;
+ this.scale = (this.canvasWidth / this.pageWidth);
var div = this.el = document.createElement('div');
div.id = 'thumbnailContainer' + id;
div.className = 'thumbnail';
if (id === 1) {
// Highlight the thumbnail of the first page when no page number is
// specified (or exists in cache) when the document is loaded.
div.classList.add('selected');
}
var ring = document.createElement('div');
ring.className = 'thumbnailSelectionRing';
- ring.style.width = canvasWidth + 'px';
- ring.style.height = canvasHeight + 'px';
+ ring.style.width = this.canvasWidth + 'px';
+ ring.style.height = this.canvasHeight + 'px';
div.appendChild(ring);
anchor.appendChild(div);
container.appendChild(anchor);
this.hasImage = false;
this.renderingState = RenderingStates.INITIAL;
- this.updateRotation = function(rot) {
-
- rotation = rot;
- totalRotation = (rotation + pdfPage.rotate) % 360;
- viewport = pdfPage.getViewport(1, totalRotation);
- pageWidth = this.width = viewport.width;
- pageHeight = this.height = viewport.height;
- pageRatio = pageWidth / pageHeight;
-
- canvasHeight = canvasWidth / this.width * this.height;
- scaleX = this.scaleX = (canvasWidth / pageWidth);
- scaleY = this.scaleY = (canvasHeight / pageHeight);
+ this.setPdfPage = function thumbnailViewSetPdfPage(pdfPage) {
+ this.pdfPage = pdfPage;
+ this.pdfPageRotate = pdfPage.rotate;
+ this.viewport = pdfPage.getViewport(1);
+ this.update();
+ };
+
+ this.update = function thumbnailViewUpdate(rot) {
+ if (!this.pdfPage) {
+ return;
+ }
+
+ if (rot !== undefined) {
+ this.rotation = rot;
+ }
+
+ var totalRotation = (this.rotation + this.pdfPage.rotate) % 360;
+ this.viewport = this.viewport.clone({
+ scale: 1,
+ rotation: totalRotation
+ });
+ this.pageWidth = this.viewport.width;
+ this.pageHeight = this.viewport.height;
+ this.pageRatio = this.pageWidth / this.pageHeight;
+
+ this.canvasHeight = this.canvasWidth / this.pageWidth * this.pageHeight;
+ this.scale = (this.canvasWidth / this.pageWidth);
div.removeAttribute('data-loaded');
ring.textContent = '';
- ring.style.width = canvasWidth + 'px';
- ring.style.height = canvasHeight + 'px';
+ ring.style.width = this.canvasWidth + 'px';
+ ring.style.height = this.canvasHeight + 'px';
this.hasImage = false;
this.renderingState = RenderingStates.INITIAL;
this.resume = null;
};
- function getPageDrawContext() {
+ this.getPageDrawContext = function thumbnailViewGetPageDrawContext() {
var canvas = document.createElement('canvas');
canvas.id = 'thumbnail' + id;
- canvas.width = canvasWidth;
- canvas.height = canvasHeight;
+ canvas.width = this.canvasWidth;
+ canvas.height = this.canvasHeight;
canvas.className = 'thumbnailImage';
canvas.setAttribute('aria-label', mozL10n.get('thumb_page_canvas',
{page: id}, 'Thumbnail of Page {{page}}'));
div.setAttribute('data-loaded', true);
ring.appendChild(canvas);
var ctx = canvas.getContext('2d');
ctx.save();
ctx.fillStyle = 'rgb(255, 255, 255)';
- ctx.fillRect(0, 0, canvasWidth, canvasHeight);
+ ctx.fillRect(0, 0, this.canvasWidth, this.canvasHeight);
ctx.restore();
return ctx;
- }
+ };
this.drawingRequired = function thumbnailViewDrawingRequired() {
return !this.hasImage;
};
this.draw = function thumbnailViewDraw(callback) {
+ if (!this.pdfPage) {
+ var promise = PDFView.getPage(this.id);
+ promise.then(function(pdfPage) {
+ this.setPdfPage(pdfPage);
+ this.draw(callback);
+ }.bind(this));
+ return;
+ }
+
if (this.renderingState !== RenderingStates.INITIAL) {
console.error('Must be in new state before drawing');
}
this.renderingState = RenderingStates.RUNNING;
if (this.hasImage) {
callback();
return;
}
var self = this;
- var ctx = getPageDrawContext();
- var drawViewport = pdfPage.getViewport(scaleX, totalRotation);
+ var ctx = this.getPageDrawContext();
+ var drawViewport = this.viewport.clone({ scale: this.scale });
var renderContext = {
canvasContext: ctx,
viewport: drawViewport,
continueCallback: function(cont) {
if (PDFView.highestPriorityPage !== 'thumbnail' + self.id) {
self.renderingState = RenderingStates.PAUSED;
self.resume = function() {
self.renderingState = RenderingStates.RUNNING;
cont();
};
return;
}
cont();
}
};
- pdfPage.render(renderContext).then(
+ this.pdfPage.render(renderContext).then(
function pdfPageRenderCallback() {
self.renderingState = RenderingStates.FINISHED;
callback();
},
function pdfPageRenderError(error) {
self.renderingState = RenderingStates.FINISHED;
callback();
}
);
this.hasImage = true;
};
this.setImage = function thumbnailViewSetImage(img) {
if (this.hasImage || !img)
return;
this.renderingState = RenderingStates.FINISHED;
- var ctx = getPageDrawContext();
+ var ctx = this.getPageDrawContext();
ctx.drawImage(img, 0, 0, img.width, img.height,
0, 0, ctx.canvas.width, ctx.canvas.height);
this.hasImage = true;
};
};
var DocumentOutlineView = function documentOutlineView(outline) {
--- a/browser/locales/en-US/pdfviewer/viewer.properties
+++ b/browser/locales/en-US/pdfviewer/viewer.properties
@@ -116,9 +116,10 @@ missing_file_error=Missing PDF file.
# LOCALIZATION NOTE (text_annotation_type): This is used as a tooltip.
# "{{type}}" will be replaced with an annotation type from a list defined in
# the PDF spec (32000-1:2008 Table 169 – Annotation types).
# Some common types are e.g.: "Check", "Text", "Comment", "Note"
text_annotation_type=[{{type}} Annotation]
request_password=PDF is protected by a password:
printing_not_supported=Warning: Printing is not fully supported by this browser.
+printing_not_ready=Warning: The PDF is not fully loaded for printing.
web_fonts_disabled=Web fonts are disabled: unable to use embedded PDF fonts.
--- a/browser/metro/base/tests/Makefile.in
+++ b/browser/metro/base/tests/Makefile.in
@@ -5,52 +5,20 @@
DEPTH = @DEPTH@
topsrcdir = @top_srcdir@
srcdir = @srcdir@
VPATH = @srcdir@
relativesrcdir = @relativesrcdir@
include $(DEPTH)/config/autoconf.mk
-BROWSER_TESTS = \
- head.js \
- browser_test.js \
- browser_canonizeURL.js \
- browser_context_ui.js \
- browser_tiles.js \
- browser_tilegrid.xul \
- browser_onscreen_keyboard.js \
- browser_onscreen_keyboard.html \
- browser_remotetabs.js \
- browser_downloads.js \
- browser_plugin_input.html \
- browser_plugin_input_mouse.js \
- browser_plugin_input_keyboard.js \
- browser_context_menu_tests.js \
- browser_sanitize_ui.js \
- browser_context_menu_tests_01.html \
- browser_context_menu_tests_02.html \
- browser_context_menu_tests_03.html \
- text-block.html \
- browser_topsites.js \
- $(NULL)
-BROWSER_TEST_RESOURCES = \
- res/image01.png \
- $(NULL)
-
XPCSHELL_TESTS = unit
# For now we're copying the actual Util code.
# We should make this into a jsm module. See bug 848137
XPCSHELL_RESOURCES = \
$(DEPTH)/browser/metro/base/content/Util.js \
$(NULL)
-libs:: $(BROWSER_TESTS)
- $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/metro/$(relativesrcdir)
-
-libs:: $(BROWSER_TEST_RESOURCES)
- $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/metro/$(relativesrcdir)/res
-
libs:: $(XPCSHELL_RESOURCES)
$(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/xpcshell/$(relativesrcdir)/unit/
include $(topsrcdir)/config/rules.mk
deleted file mode 100644
--- a/browser/metro/base/tests/addons/browser_install1_1/bootstrap.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-function install(data, reason) {}
-function startup(data, reason) {}
-function shutdown(data, reason) {}
-function uninstall(data, reason) {}
-
deleted file mode 100644
--- a/browser/metro/base/tests/addons/browser_install1_1/install.rdf
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
- <Description about="urn:mozilla:install-manifest">
- <em:id>addon1@tests.mozilla.org</em:id>
- <em:version>1.0</em:version>
- <em:updateURL>http://example.com/browser/mobile/chrome/tests/browser_upgrade.rdf</em:updateURL>
- <em:bootstrap>true</em:bootstrap>
-
- <em:targetApplication>
- <Description>
- <em:id>toolkit@mozilla.org</em:id>
- <em:minVersion>0</em:minVersion>
- <em:maxVersion>*</em:maxVersion>
- </Description>
- </em:targetApplication>
-
- <!-- Front End MetaData -->
- <em:name>Install Tests</em:name>
-
- </Description>
-</RDF>
deleted file mode 100644
--- a/browser/metro/base/tests/addons/browser_install1_2/install.rdf
+++ /dev/null
@@ -1,22 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
- <Description about="urn:mozilla:install-manifest">
- <em:id>addon2@tests.mozilla.org</em:id>
- <em:version>2.0</em:version>
-
- <em:targetApplication>
- <Description>
- <em:id>toolkit@mozilla.org</em:id>
- <em:minVersion>0</em:minVersion>
- <em:maxVersion>*</em:maxVersion>
- </Description>
- </em:targetApplication>
-
- <!-- Front End MetaData -->
- <em:name>Install Tests 2</em:name>
-
- </Description>
-</RDF>
deleted file mode 100644
--- a/browser/metro/base/tests/addons/browser_install1_3/install.rdf
+++ /dev/null
@@ -1,23 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
- <Description about="urn:mozilla:install-manifest">
- <em:id>addon1@tests.mozilla.org</em:id>
- <em:version>3.0</em:version>
- <em:updateURL>http://example.com/browser/mobile/chrome/tests/browser_upgrade.rdf</em:updateURL>
-
- <em:targetApplication>
- <Description>
- <em:id>toolkit@mozilla.org</em:id>
- <em:minVersion>0</em:minVersion>
- <em:maxVersion>*</em:maxVersion>
- </Description>
- </em:targetApplication>
-
- <!-- Front End MetaData -->
- <em:name>Install Tests</em:name>
-
- </Description>
-</RDF>
deleted file mode 100644
--- a/browser/metro/base/tests/addons/browser_locale1/boostrap.js
+++ /dev/null
@@ -1,9 +0,0 @@
-/* Any copyright is dedicated to the Public Domain.
- * http://creativecommons.org/publicdomain/zero/1.0/
- */
-
-function install(data, reason) {}
-function startup(data, reason) {}
-function shutdown(data, reason) {}
-function uninstall(data, reason) {}
-
deleted file mode 100644
--- a/browser/metro/base/tests/addons/browser_locale1/chrome.manifest
+++ /dev/null
@@ -1,4 +0,0 @@
-locale mozapps te-st chrome # locale
-locale browser te-st chrome # duplicate locale
-locale browser te-st-a chrome # second locale
-locale branding te-st-3 chrome # wrong component
deleted file mode 100644
--- a/browser/metro/base/tests/addons/browser_locale1/install.rdf
+++ /dev/null
@@ -1,24 +0,0 @@
-<?xml version="1.0"?>
-
-<RDF xmlns="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
- xmlns:em="http://www.mozilla.org/2004/em-rdf#">
-
- <Description about="urn:mozilla:install-manifest">
- <em:id>locale1@tests.mozilla.org</em:id>
- <em:version>1.0</em:version>
- <em:type>8</em:type>
- <em:bootstrap>true</em:bootstrap>
-
- <em:targetApplication>
- <Description>
- <em:id>toolkit@mozilla.org</em:id>
- <em:minVersion>0</em:minVersion>
- <em:maxVersion>*</em:maxVersion>
- </Description>
- </em:targetApplication>
-
- <!-- Front End MetaData -->
- <em:name>Test Locale</em:name>
-
- </Description>
-</RDF>
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/Makefile.in
@@ -0,0 +1,46 @@
+# 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/.
+
+DEPTH = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir = @srcdir@
+VPATH = @srcdir@
+relativesrcdir = @relativesrcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+BROWSER_TESTS = \
+ head.js \
+ browser_test.js \
+ browser_canonizeURL.js \
+ browser_context_ui.js \
+ browser_tiles.js \
+ browser_tilegrid.xul \
+ browser_onscreen_keyboard.js \
+ browser_onscreen_keyboard.html \
+ browser_remotetabs.js \
+ browser_downloads.js \
+ browser_plugin_input.html \
+ browser_plugin_input_mouse.js \
+ browser_plugin_input_keyboard.js \
+ browser_context_menu_tests.js \
+ browser_context_menu_tests_01.html \
+ browser_context_menu_tests_02.html \
+ browser_context_menu_tests_03.html \
+ text-block.html \
+ browser_sanitize_ui.js \
+ browser_topsites.js \
+ $(NULL)
+
+BROWSER_TEST_RESOURCES = \
+ res/image01.png \
+ $(NULL)
+
+libs:: $(BROWSER_TESTS)
+ $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/metro/$(relativesrcdir)
+
+libs:: $(BROWSER_TEST_RESOURCES)
+ $(INSTALL) $(foreach f,$^,"$f") $(DEPTH)/_tests/testing/mochitest/metro/$(relativesrcdir)/res
+
+include $(topsrcdir)/config/rules.mk
rename from browser/metro/base/tests/browser_canonizeURL.js
rename to browser/metro/base/tests/mochitest/browser_canonizeURL.js
rename from browser/metro/base/tests/browser_context_menu_tests.js
rename to browser/metro/base/tests/mochitest/browser_context_menu_tests.js
rename from browser/metro/base/tests/browser_context_menu_tests_01.html
rename to browser/metro/base/tests/mochitest/browser_context_menu_tests_01.html
rename from browser/metro/base/tests/browser_context_menu_tests_02.html
rename to browser/metro/base/tests/mochitest/browser_context_menu_tests_02.html
rename from browser/metro/base/tests/browser_context_menu_tests_03.html
rename to browser/metro/base/tests/mochitest/browser_context_menu_tests_03.html
rename from browser/metro/base/tests/browser_context_ui.js
rename to browser/metro/base/tests/mochitest/browser_context_ui.js
rename from browser/metro/base/tests/browser_downloads.js
rename to browser/metro/base/tests/mochitest/browser_downloads.js
rename from browser/metro/base/tests/browser_onscreen_keyboard.html
rename to browser/metro/base/tests/mochitest/browser_onscreen_keyboard.html
rename from browser/metro/base/tests/browser_onscreen_keyboard.js
rename to browser/metro/base/tests/mochitest/browser_onscreen_keyboard.js
rename from browser/metro/base/tests/browser_plugin_input.html
rename to browser/metro/base/tests/mochitest/browser_plugin_input.html
rename from browser/metro/base/tests/browser_plugin_input_keyboard.js
rename to browser/metro/base/tests/mochitest/browser_plugin_input_keyboard.js
rename from browser/metro/base/tests/browser_plugin_input_mouse.js
rename to browser/metro/base/tests/mochitest/browser_plugin_input_mouse.js
rename from browser/metro/base/tests/browser_remotetabs.js
rename to browser/metro/base/tests/mochitest/browser_remotetabs.js
rename from browser/metro/base/tests/browser_sanitize_ui.js
rename to browser/metro/base/tests/mochitest/browser_sanitize_ui.js
rename from browser/metro/base/tests/browser_test.js
rename to browser/metro/base/tests/mochitest/browser_test.js
rename from browser/metro/base/tests/browser_tilegrid.xul
rename to browser/metro/base/tests/mochitest/browser_tilegrid.xul
rename from browser/metro/base/tests/browser_tiles.js
rename to browser/metro/base/tests/mochitest/browser_tiles.js
rename from browser/metro/base/tests/browser_topsites.js
rename to browser/metro/base/tests/mochitest/browser_topsites.js
rename from browser/metro/base/tests/head.js
rename to browser/metro/base/tests/mochitest/head.js
new file mode 100644
--- /dev/null
+++ b/browser/metro/base/tests/mochitest/moz.build
@@ -0,0 +1,4 @@
+# vim: set filetype=python:
+# 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/.
rename from browser/metro/base/tests/res/image01.png
rename to browser/metro/base/tests/mochitest/res/image01.png
rename from browser/metro/base/tests/text-block.html
rename to browser/metro/base/tests/mochitest/text-block.html
--- a/browser/metro/base/tests/moz.build
+++ b/browser/metro/base/tests/moz.build
@@ -1,5 +1,6 @@
# vim: set filetype=python:
# 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/.
+TEST_DIRS += ['mochitest']
--- a/browser/metro/shell/testing/metrotestharness.cpp
+++ b/browser/metro/shell/testing/metrotestharness.cpp
@@ -151,16 +151,24 @@ public:
~DeleteTestFileHelper() {
if (mTestFile.GetLength()) {
Log(L"Deleting %s", CStringW(mTestFile));
DeleteFileA(mTestFile);
}
}
};
+static void AddConsoleIdToParams()
+{
+ DWORD dwId = GetCurrentProcessId();
+ CString tmp;
+ tmp.Format(L" testconsoleid=%d", dwId);
+ sAppParams += tmp;
+}
+
static bool Launch()
{
Log(L"Launching browser...");
DWORD processID;
// The interface that allows us to activate the browser
CComPtr<IApplicationActivationManager> activateMgr;
@@ -294,14 +302,15 @@ int wmain(int argc, WCHAR* argv[])
sAppParams.Append(argv[idx]);
sAppParams.Append(L" ");
}
sAppParams.Trim();
if (sFirefoxPath.GetLength()) {
Log(L"firefoxpath: '%s'", sFirefoxPath);
}
+ AddConsoleIdToParams();
Log(L"args: '%s'", sAppParams);
Launch();
CoUninitialize();
return 0;
}
--- a/content/base/public/nsIDOMDataChannel.idl
+++ b/content/base/public/nsIDOMDataChannel.idl
@@ -1,24 +1,27 @@
#include "domstubs.idl"
#include "nsIDOMEventTarget.idl"
interface nsIVariant;
-[scriptable, builtinclass, uuid(bb47d50e-48ab-464d-b665-5ea47382e8d6)]
+[scriptable, builtinclass, uuid(1aed816d-1156-414e-8fe2-f01daa6021f0)]
interface nsIDOMDataChannel : nsIDOMEventTarget
{
readonly attribute DOMString label;
+ readonly attribute DOMString protocol;
readonly attribute boolean reliable;
readonly attribute boolean ordered;
readonly attribute DOMString readyState;
readonly attribute unsigned long bufferedAmount;
+ readonly attribute unsigned short stream;
+
[implicit_jscontext] attribute jsval onopen;
[implicit_jscontext] attribute jsval onerror;
[implicit_jscontext] attribute jsval onclose;
[implicit_jscontext] attribute jsval onmessage;
attribute DOMString binaryType;
void close();
--- a/content/base/src/nsDOMDataChannel.cpp
+++ b/content/base/src/nsDOMDataChannel.cpp
@@ -184,16 +184,30 @@ NS_IMPL_EVENT_HANDLER(nsDOMDataChannel,
NS_IMETHODIMP
nsDOMDataChannel::GetLabel(nsAString& aLabel)
{
mDataChannel->GetLabel(aLabel);
return NS_OK;
}
+NS_IMETHODIMP
+nsDOMDataChannel::GetProtocol(nsAString& aProtocol)
+{
+ mDataChannel->GetProtocol(aProtocol);
+ return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataChannel::GetStream(uint16_t *aStream)
+{
+ mDataChannel->GetStream(aStream);
+ return NS_OK;
+}
+
// XXX should be GetType()? Open question for the spec
NS_IMETHODIMP
nsDOMDataChannel::GetReliable(bool* aReliable)
{
*aReliable = (mDataChannel->GetType() == mozilla::DataChannelConnection::RELIABLE);
return NS_OK;
}
--- a/content/base/test/chrome/test_csp_bug768029.html
+++ b/content/base/test/chrome/test_csp_bug768029.html
@@ -24,17 +24,17 @@ Components.utils.import("resource://gre/
// Note: we don't have to inspect all the different operations of CSP,
// we're just looking for specific differences in behavior that indicate
// a default CSP got applied.
const DEFAULT_CSP_PRIV = "default-src *; script-src 'self'; style-src 'self' 'unsafe-inline'; object-src 'none'";
const DEFAULT_CSP_CERT = "default-src *; script-src 'self'; style-src 'self'; object-src 'none'";
if (navigator.platform.startsWith("Linux")) {
- SimpleTest.expectAssertions(0, 1);
+ SimpleTest.expectAssertions(0, 1); // bug 846137, ASSERTION: wrong thread: 'PR_GetCurrentThread() == gSocketThread'
}
SimpleTest.waitForExplicitFinish();
var gData = [
{
app: "https://example.com/manifest.webapp",
appStatus: Components.interfaces.nsIPrincipal.APP_STATUS_INSTALLED,
origin: "https://example.com",
--- a/content/svg/content/src/SVGAnimationElement.h
+++ b/content/svg/content/src/SVGAnimationElement.h
@@ -31,16 +31,18 @@ protected:
public:
// interfaces:
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_CYCLE_COLLECTION_CLASS_INHERITED(SVGAnimationElement,
SVGAnimationElementBase)
+ virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE = 0;
+
// nsIContent specializations
virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
nsIContent* aBindingParent,
bool aCompileEventHandlers);
virtual void UnbindFromTree(bool aDeep, bool aNullParent);
virtual nsresult UnsetAttr(int32_t aNamespaceID, nsIAtom* aAttribute,
bool aNotify);
--- a/content/svg/content/src/SVGGradientElement.h
+++ b/content/svg/content/src/SVGGradientElement.h
@@ -43,16 +43,18 @@ class SVGGradientElement : public SVGGra
friend class ::nsSVGGradientFrame;
protected:
SVGGradientElement(already_AddRefed<nsINodeInfo> aNodeInfo);
virtual JSObject*
WrapNode(JSContext* aCx, JSObject* aScope) MOZ_OVERRIDE = 0;
public:
+ virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE = 0;
+
// nsIContent
NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
virtual SVGAnimatedTransformList*
GetAnimatedTransformList(uint32_t aFlags = 0);
virtual nsIAtom* GetTransformListAttrName() const {
return nsGkAtoms::gradientTransform;
}
--- a/content/svg/content/src/SVGTextContentElement.cpp
+++ b/content/svg/content/src/SVGTextContentElement.cpp
@@ -62,16 +62,30 @@ SVGTextContentElement::GetComputedTextLe
nsSVGTextFrame2* textFrame = GetSVGTextFrame();
return textFrame ? textFrame->GetComputedTextLength(this) : 0.0f;
} else {
nsSVGTextContainerFrame* metrics = GetTextContainerFrame();
return metrics ? metrics->GetComputedTextLength() : 0.0f;
}
}
+void
+SVGTextContentElement::SelectSubString(uint32_t charnum, uint32_t nchars, ErrorResult& rv)
+{
+ if (FrameIsSVGText()) {
+ nsSVGTextFrame2* textFrame = GetSVGTextFrame();
+ if (!textFrame)
+ return;
+
+ rv = textFrame->SelectSubString(this, charnum, nchars);
+ } else {
+ rv.Throw(NS_ERROR_NOT_IMPLEMENTED);
+ }
+}
+
float
SVGTextContentElement::GetSubStringLength(uint32_t charnum, uint32_t nchars, ErrorResult& rv)
{
if (FrameIsSVGText()) {
nsSVGTextFrame2* textFrame = GetSVGTextFrame();
if (!textFrame)
return 0.0f;
--- a/content/svg/content/src/SVGTextContentElement.h
+++ b/content/svg/content/src/SVGTextContentElement.h
@@ -23,16 +23,17 @@ typedef SVGGraphicsElement SVGTextConten
class SVGTextContentElement : public SVGTextContentElementBase
{
public:
using FragmentOrElement::TextLength;
// WebIDL
int32_t GetNumberOfChars();
float GetComputedTextLength();
+ void SelectSubString(uint32_t charnum, uint32_t nchars, ErrorResult& rv);
float GetSubStringLength(uint32_t charnum, uint32_t nchars, ErrorResult& rv);
already_AddRefed<nsISVGPoint> GetStartPositionOfChar(uint32_t charnum, ErrorResult& rv);
already_AddRefed<nsISVGPoint> GetEndPositionOfChar(uint32_t charnum, ErrorResult& rv);
already_AddRefed<SVGIRect> GetExtentOfChar(uint32_t charnum, ErrorResult& rv);
float GetRotationOfChar(uint32_t charnum, ErrorResult& rv);
int32_t GetCharNumAtPosition(nsISVGPoint& point);
protected:
--- a/content/svg/content/src/SVGTransformableElement.h
+++ b/content/svg/content/src/SVGTransformableElement.h
@@ -21,16 +21,18 @@ class SVGIRect;
class SVGTransformableElement : public nsSVGElement
{
public:
SVGTransformableElement(already_AddRefed<nsINodeInfo> aNodeInfo)
: nsSVGElement(aNodeInfo) {}
virtual ~SVGTransformableElement() {}
+ virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE = 0;
+
// WebIDL
already_AddRefed<DOMSVGAnimatedTransformList> Transform();
nsSVGElement* GetNearestViewportElement();
nsSVGElement* GetFarthestViewportElement();
already_AddRefed<SVGIRect> GetBBox(ErrorResult& rv);
already_AddRefed<SVGMatrix> GetCTM();
already_AddRefed<SVGMatrix> GetScreenCTM();
already_AddRefed<SVGMatrix> GetTransformToElement(SVGGraphicsElement& aElement,
--- a/content/svg/content/src/nsSVGFilters.h
+++ b/content/svg/content/src/nsSVGFilters.h
@@ -132,16 +132,18 @@ public:
// interfaces:
NS_DECL_ISUPPORTS_INHERITED
// nsIContent interface
NS_IMETHOD_(bool) IsAttributeMapped(const nsIAtom* aAttribute) const;
// nsSVGElement interface
+ virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE = 0;
+
virtual bool HasValidDimensions() const;
bool IsNodeOfType(uint32_t aFlags) const
{ return !(aFlags & ~(eCONTENT | eFILTER)); }
virtual nsSVGString& GetResultImageName() = 0;
// Return a list of all image names used as sources. Default is to
// return no sources.
@@ -234,16 +236,18 @@ typedef nsSVGElement SVGFEUnstyledElemen
class SVGFEUnstyledElement : public SVGFEUnstyledElementBase
{
protected:
SVGFEUnstyledElement(already_AddRefed<nsINodeInfo> aNodeInfo)
: SVGFEUnstyledElementBase(aNodeInfo) {}
public:
+ virtual nsresult Clone(nsINodeInfo *aNodeInfo, nsINode **aResult) const MOZ_OVERRIDE = 0;
+
// returns true if changes to the attribute should cause us to
// repaint the filter
virtual bool AttributeAffectsRendering(
int32_t aNameSpaceID, nsIAtom* aAttribute) const = 0;
};
//------------------------------------------------------------
copy from content/svg/content/test/getSubStringLength-helper.svg
copy to content/svg/content/test/selectSubString-helper.svg
copy from content/svg/content/test/test_getSubStringLength.xhtml
copy to content/svg/content/test/test_selectSubString.xhtml
--- a/content/svg/content/test/test_getSubStringLength.xhtml
+++ b/content/svg/content/test/test_selectSubString.xhtml
@@ -1,107 +1,72 @@
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">
<!--
-https://bugzilla.mozilla.org/show_bug.cgi?id=420243
+https://bugzilla.mozilla.org/show_bug.cgi?id=398825
-->
<head>
- <title>Test for Bug 420243</title>
+ <title>Test for Bug 398825</title>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body>
-<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=420243">Mozilla Bug 420243</a>
+<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=398825">Mozilla Bug 398825</a>
<p id="display"></p>
<div id="content" style="display: none"></div>
-<iframe id="svg" src="getSubStringLength-helper.svg"></iframe>
+<iframe id="svg" src="selectSubString-helper.svg"></iframe>
<pre id="test">
<script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish();
-function runTests(text, charWidth)
+function runTests()
{
- function chars(n) { return charWidth * n; }
+ var document = $("svg").contentWindow.document;
+ var text = document.getElementById("text");
function expectThrow(charnum, nchars)
{
try
{
- text.getSubStringLength(charnum, nchars);
+ text.selectSubString(charnum, nchars);
ok(false,
- "text.getSubStringLength(" + charnum + "," + nchars + ") " +
- "should have thrown");
+ "text.selectSubString(" + charnum + "," + nchars + ") " +
+ "should have thrown");
}
catch (e)
{
is(e.name, "IndexSizeError",
- "expected an index error for " +
- "text.getSubStringLength(" + charnum + "," + nchars + ")");
+ "expected an index error for " +
+ "text.selectSubString(" + charnum + "," + nchars + ")");
is(e.code, DOMException.INDEX_SIZE_ERR,
- "expected an index error for " +
- "text.getSubStringLength(" + charnum + "," + nchars + ")");
+ "expected an index error for " +
+ "text.selectSubString(" + charnum + "," + nchars + ")");
}
}
- function expectValue(charnum, nchars, expected)
- {
- try
- {
- is(text.getSubStringLength(charnum, nchars), expected,
- "text.getSubStringLength(" + charnum + "," + nchars + ") " +
- "returned wrong value");
- }
- catch (e)
- {
- ok(false,
- "unexpected exception for " +
- "text.getSubStringLength(" + charnum + "," + nchars + ")");
- }
- }
-
expectThrow(-1, 2);
expectThrow(-1, 0);
expectThrow(1, 3);
expectThrow(0, 4);
expectThrow(3, 0);
- expectValue(0, 0, chars(0));
- expectValue(1, 0, chars(0));
- expectValue(2, 0, chars(0));
- expectValue(0, 1, chars(1));
- expectValue(1, 1, chars(1));
- expectValue(2, 1, chars(1));
- expectValue(0, 2, chars(2));
- expectValue(1, 2, chars(2));
- expectValue(0, 3, chars(3));
-
expectThrow(1, -1);
expectThrow(2, -1);
expectThrow(3, -1);
expectThrow(3, -3);
expectThrow(-1, -1);
+
+ SimpleTest.finish();
}
-
function run()
{
- try
- {
- var document = $("svg").contentWindow.document;
- var text = document.getElementById("text");
-
- runTests(text, text.getSubStringLength(0, 1));
- }
- catch (e)
- {
- ok(false, "threw error: " + e);
- }
-
- SimpleTest.finish();
+ SpecialPowers.pushPrefEnv({ set: [['svg.text.css-frames.enabled', true]] },
+ runTests);
}
window.addEventListener("load", run, false);
</script>
</pre>
</body>
</html>
--- a/dom/indexedDB/CheckPermissionsHelper.cpp
+++ b/dom/indexedDB/CheckPermissionsHelper.cpp
@@ -10,16 +10,17 @@
#include "nsILoadContext.h"
#include "nsIWebNavigation.h"
#include "nsIObserverService.h"
#include "nsIPermissionManager.h"
#include "nsIPrincipal.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIURI.h"
+#include "CheckQuotaHelper.h"
#include "nsContentUtils.h"
#include "nsDOMStorage.h"
#include "nsNetUtil.h"
#include "nsThreadUtils.h"
#include "mozilla/dom/quota/QuotaManager.h"
#include "mozilla/Preferences.h"
#include "mozilla/Services.h"
@@ -33,19 +34,20 @@
// This is a little confusing, but our default behavior (UNKNOWN_ACTION) is to
// allow access without a prompt. If the "indexedDB" permission is set to
// ALLOW_ACTION then we will issue a prompt before allowing access. Otherwise
// (DENY_ACTION) we deny access.
#define PERMISSION_ALLOWED nsIPermissionManager::UNKNOWN_ACTION
#define PERMISSION_DENIED nsIPermissionManager::DENY_ACTION
#define PERMISSION_PROMPT nsIPermissionManager::ALLOW_ACTION
-using namespace mozilla;
USING_INDEXEDDB_NAMESPACE
using namespace mozilla::services;
+using mozilla::dom::quota::CheckQuotaHelper;
+using mozilla::Preferences;
namespace {
inline
uint32_t
GetIndexedDBPermissions(nsIDOMWindow* aWindow)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
@@ -106,24 +108,29 @@ CheckPermissionsHelper::Run()
nsresult rv;
if (mHasPrompted) {
// Add permissions to the database, but only if we are in the parent
// process (if we are in the child process, we have already
// set the permission when the prompt was shown in the parent, as
// we cannot set the permission from the child).
if (permission != PERMISSION_PROMPT &&
IndexedDatabaseManager::IsMainProcess()) {
+ NS_ASSERTION(mWindow, "Null window!");
+
+ nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow);
+ NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!");
+
+ nsIPrincipal* windowPrincipal = sop->GetPrincipal();
+ NS_ASSERTION(windowPrincipal, "Null principal!");
+
nsCOMPtr<nsIPermissionManager> permissionManager =
do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
NS_ENSURE_STATE(permissionManager);
- nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(mWindow);
- NS_ENSURE_TRUE(sop, NS_ERROR_FAILURE);
-
- rv = permissionManager->AddFromPrincipal(sop->GetPrincipal(),
+ rv = permissionManager->AddFromPrincipal(windowPrincipal,
PERMISSION_INDEXEDDB, permission,
nsIPermissionManager::EXPIRE_NEVER,
0);
NS_ENSURE_SUCCESS(rv, rv);
}
}
else if (permission == PERMISSION_PROMPT && mPromptAllowed) {
nsCOMPtr<nsIObserverService> obs = GetObserverService();
@@ -136,16 +143,34 @@ CheckPermissionsHelper::Run()
nsRefPtr<OpenDatabaseHelper> helper;
helper.swap(mHelper);
nsCOMPtr<nsIDOMWindow> window;
window.swap(mWindow);
if (permission == PERMISSION_ALLOWED) {
+ // If we're running from a window then we should check the quota permission
+ // as well. If we don't have a window then we're opening a chrome database
+ // and the quota will be unlimited already.
+ if (window) {
+ nsCOMPtr<nsIScriptObjectPrincipal> sop = do_QueryInterface(window);
+ NS_ASSERTION(sop, "Window didn't QI to nsIScriptObjectPrincipal!");
+
+ nsIPrincipal* windowPrincipal = sop->GetPrincipal();
+ NS_ASSERTION(windowPrincipal, "Null principal!");
+
+ uint32_t quotaPermission =
+ CheckQuotaHelper::GetQuotaPermission(windowPrincipal);
+
+ if (quotaPermission == nsIPermissionManager::ALLOW_ACTION) {
+ helper->SetUnlimitedQuotaAllowed();
+ }
+ }
+
quota::QuotaManager* quotaManager = quota::QuotaManager::Get();
NS_ASSERTION(quotaManager, "This should never be null!");
return helper->Dispatch(quotaManager->IOThread());
}
NS_ASSERTION(permission == PERMISSION_PROMPT ||
permission == PERMISSION_DENIED,
--- a/dom/indexedDB/IDBObjectStore.cpp
+++ b/dom/indexedDB/IDBObjectStore.cpp
@@ -2686,32 +2686,35 @@ inline nsresult
CopyData(nsIInputStream* aInputStream, nsIOutputStream* aOutputStream)
{
nsresult rv;
do {
char copyBuffer[FILE_COPY_BUFFER_SIZE];
uint32_t numRead;
- rv = aInputStream->Read(copyBuffer, FILE_COPY_BUFFER_SIZE, &numRead);
- NS_ENSURE_SUCCESS(rv, rv);
-
- if (numRead <= 0) {
+ rv = aInputStream->Read(copyBuffer, sizeof(copyBuffer), &numRead);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+ if (!numRead) {
break;
}
uint32_t numWrite;
rv = aOutputStream->Write(copyBuffer, numRead, &numWrite);
- NS_ENSURE_SUCCESS(rv, rv);
-
- NS_ENSURE_TRUE(numWrite == numRead, NS_ERROR_FAILURE);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+
+ if (numWrite < numRead) {
+ // Must have hit the quota limit.
+ return NS_ERROR_DOM_INDEXEDDB_QUOTA_ERR;
+ }
} while (true);
rv = aOutputStream->Flush();
- NS_ENSURE_SUCCESS(rv, rv);
+ NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
return NS_OK;
}
void
ObjectStoreHelper::ReleaseMainThreadObjects()
{
mObjectStore = nullptr;
@@ -2909,17 +2912,17 @@ AddHelper::DoDatabaseWork(mozIStorageCon
nativeFile = fileManager->GetFileForId(directory, id);
NS_ENSURE_TRUE(nativeFile, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
nsRefPtr<FileOutputStream> outputStream = FileOutputStream::Create(
mObjectStore->Transaction()->Database()->Origin(), nativeFile);
NS_ENSURE_TRUE(outputStream, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
rv = CopyData(inputStream, outputStream);
- NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
+ NS_ENSURE_SUCCESS(rv, rv);
cloneFile.mFile->AddFileInfo(fileInfo);
}
if (index) {
fileIds.Append(NS_LITERAL_STRING(" "));
}
fileIds.AppendInt(id);
--- a/dom/indexedDB/Makefile.in
+++ b/dom/indexedDB/Makefile.in
@@ -67,16 +67,17 @@ EXPORTS_mozilla/dom/indexedDB = \
LOCAL_INCLUDES = \
-I$(topsrcdir)/caps/include \
-I$(topsrcdir)/content/base/src \
-I$(topsrcdir)/content/events/src \
-I$(topsrcdir)/db/sqlite3/src \
-I$(topsrcdir)/dom/base \
-I$(topsrcdir)/dom/src/storage \
+ -I$(topsrcdir)/dom/quota \
-I$(topsrcdir)/xpcom/build \
$(NULL)
DEFINES += -D_IMPL_NS_LAYOUT
# Make sure to quickstub as much as possible here! See
# js/xpconnect/src/dom_quickstubs.qsconf.
include $(topsrcdir)/config/config.mk
--- a/dom/indexedDB/OpenDatabaseHelper.cpp
+++ b/dom/indexedDB/OpenDatabaseHelper.cpp
@@ -1646,17 +1646,17 @@ OpenDatabaseHelper::DoDatabaseWork()
nsCOMPtr<nsIFile> dbDirectory;
QuotaManager* quotaManager = QuotaManager::Get();
NS_ASSERTION(quotaManager, "This should never be null!");
nsresult rv =
quotaManager->EnsureOriginIsInitialized(mASCIIOrigin,
- mPrivilege,
+ mTrackingQuota,
getter_AddRefs(dbDirectory));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
rv = dbDirectory->Append(NS_LITERAL_STRING(IDB_DIRECTORY_NAME));
NS_ENSURE_SUCCESS(rv, NS_ERROR_DOM_INDEXEDDB_UNKNOWN_ERR);
bool exists;
rv = dbDirectory->Exists(&exists);
--- a/dom/indexedDB/OpenDatabaseHelper.h
+++ b/dom/indexedDB/OpenDatabaseHelper.h
@@ -1,50 +1,58 @@
/* 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/. */
#ifndef mozilla_dom_indexeddb_opendatabasehelper_h__
#define mozilla_dom_indexeddb_opendatabasehelper_h__
#include "AsyncConnectionHelper.h"
+
+#include "nsIRunnable.h"
+
+#include "mozilla/dom/quota/StoragePrivilege.h"
+
#include "DatabaseInfo.h"
#include "IDBDatabase.h"
#include "IDBRequest.h"
-#include "nsIRunnable.h"
-
class mozIStorageConnection;
namespace mozilla {
namespace dom {
class ContentParent;
}
}
BEGIN_INDEXEDDB_NAMESPACE
+class CheckPermissionsHelper;
+
class OpenDatabaseHelper : public HelperBase
{
+ friend class CheckPermissionsHelper;
+
typedef mozilla::dom::quota::StoragePrivilege StoragePrivilege;
public:
OpenDatabaseHelper(IDBOpenDBRequest* aRequest,
const nsAString& aName,
const nsACString& aASCIIOrigin,
uint64_t aRequestedVersion,
bool aForDeletion,
mozilla::dom::ContentParent* aContentParent,
StoragePrivilege aPrivilege)
: HelperBase(aRequest), mOpenDBRequest(aRequest), mName(aName),
mASCIIOrigin(aASCIIOrigin), mRequestedVersion(aRequestedVersion),
mForDeletion(aForDeletion), mPrivilege(aPrivilege), mDatabaseId(nullptr),
mContentParent(aContentParent), mCurrentVersion(0), mLastObjectStoreId(0),
mLastIndexId(0), mState(eCreated), mResultCode(NS_OK),
- mLoadDBMetadata(false)
+ mLoadDBMetadata(false),
+ mTrackingQuota(aPrivilege != mozilla::dom::quota::Chrome)
{
NS_ASSERTION(!aForDeletion || !aRequestedVersion,
"Can't be for deletion and request a version!");
}
NS_DECL_ISUPPORTS
NS_DECL_NSIRUNNABLE
@@ -97,16 +105,22 @@ protected:
nsresult StartSetVersion();
nsresult StartDelete();
virtual nsresult GetSuccessResult(JSContext* aCx,
jsval* aVal) MOZ_OVERRIDE;
void DispatchSuccessEvent();
void DispatchErrorEvent();
virtual void ReleaseMainThreadObjects() MOZ_OVERRIDE;
+ // Called by CheckPermissionsHelper on the main thread before dispatch.
+ void SetUnlimitedQuotaAllowed()
+ {
+ mTrackingQuota = false;
+ }
+
// Methods only called on the DB thread
nsresult DoDatabaseWork();
// In-params.
nsRefPtr<IDBOpenDBRequest> mOpenDBRequest;
nsString mName;
nsCString mASCIIOrigin;
uint64_t mRequestedVersion;
@@ -135,13 +149,14 @@ protected:
};
OpenDatabaseState mState;
nsresult mResultCode;
nsRefPtr<FileManager> mFileManager;
nsRefPtr<DatabaseInfo> mDBInfo;
bool mLoadDBMetadata;
+ bool mTrackingQuota;
};
END_INDEXEDDB_NAMESPACE
#endif // mozilla_dom_indexeddb_opendatabasehelper_h__
\ No newline at end of file
--- a/dom/media/MediaManager.cpp
+++ b/dom/media/MediaManager.cpp
@@ -1267,16 +1267,17 @@ MediaManager::Observe(nsISupports* aSubj
prefs->RemoveObserver("media.navigator.video.default_minfps", this);
}
// Close off any remaining active windows.
{
MutexAutoLock lock(mMutex);
GetActiveWindows()->Clear();
mActiveCallbacks.Clear();
+ LOG(("Releasing MediaManager singleton and thread"));
sSingleton = nullptr;
}
return NS_OK;
} else if (!strcmp(aTopic, "getUserMedia:response:allow")) {
nsString key(aData);
nsRefPtr<nsRunnable> runnable;
--- a/dom/media/PeerConnection.js
+++ b/dom/media/PeerConnection.js
@@ -677,38 +677,45 @@ PeerConnection.prototype = {
state = "closed";
break;
}
return state;
},
createDataChannel: function(label, dict) {
this._checkClosed();
- if (dict &&
- dict.maxRetransmitTime != undefined &&
+ if (dict == undefined) {
+ dict = {};
+ }
+ if (dict.maxRetransmitTime != undefined &&
dict.maxRetransmitNum != undefined) {
throw new Components.Exception("Both maxRetransmitTime and maxRetransmitNum cannot be provided");
}
+ let protocol;
+ if (dict.protocol == undefined) {
+ protocol = "";
+ } else {
+ protocol = dict.protocol;
+ }
// Must determine the type where we still know if entries are undefined.
let type;
if (dict.maxRetransmitTime != undefined) {
type = Ci.IPeerConnection.kDataChannelPartialReliableTimed;
} else if (dict.maxRetransmitNum != undefined) {
type = Ci.IPeerConnection.kDataChannelPartialReliableRexmit;
} else {
type = Ci.IPeerConnection.kDataChannelReliable;
}
// Synchronous since it doesn't block.
- // TODO -- this may need to be revisited, based on how the
- // spec ends up defining data channel handling
let channel = this._pc.createDataChannel(
- label, type, dict.outOfOrderAllowed, dict.maxRetransmitTime,
- dict.maxRetransmitNum
+ label, protocol, type, dict.outOfOrderAllowed, dict.maxRetransmitTime,
+ dict.maxRetransmitNum, dict.preset ? true : false,
+ dict.stream != undefined ? dict.stream : 0xFFFF
);
return channel;
},
connectDataConnection: function(localport, remoteport, numstreams) {
if (numstreams == undefined || numstreams <= 0) {
numstreams = 16;
}
@@ -924,17 +931,20 @@ PeerConnectionObserver.prototype = {
});
} catch(e) {}
}
},
notifyDataChannel: function(channel) {
if (this._dompc.ondatachannel) {
try {
- this._dompc.ondatachannel.onCallback(channel);
+ this._dompc.ondatachannel.onCallback({
+ channel: channel,
+ __exposedProps__: { channel: "r" }
+ });
} catch(e) {}
}
},
notifyConnection: function() {
if (this._dompc.onconnection) {
try {
this._dompc.onconnection.onCallback();
--- a/dom/media/bridge/IPeerConnection.idl
+++ b/dom/media/bridge/IPeerConnection.idl
@@ -62,17 +62,17 @@ interface IPeerConnectionObserver : nsIS
/* When SDP is parsed and a candidate line is found this method is called.
* It should hook back into the media transport to notify it of ICE candidates
* listed in the SDP PeerConnectionImpl does not parse ICE candidates, just
* pulls them out of the SDP.
*/
void foundIceCandidate(in string candidate);
};
-[scriptable, uuid(2bba7b2b-e152-4ae7-b7d4-f84e41a2211b)]
+[scriptable, uuid(121ff773-949b-48b9-83b2-9a4ef908833c)]
interface IPeerConnection : nsISupports
{
const unsigned long kHintAudio = 0x00000001;
const unsigned long kHintVideo = 0x00000002;
const long kActionNone = -1;
const long kActionOffer = 0;
const long kActionAnswer = 1;
@@ -142,14 +142,15 @@ interface IPeerConnection : nsISupports
readonly attribute string localDescription;
readonly attribute string remoteDescription;
readonly attribute unsigned long iceState;
readonly attribute unsigned long readyState;
readonly attribute unsigned long sipccState;
/* Data channels */
- nsIDOMDataChannel createDataChannel(in ACString label,
+ nsIDOMDataChannel createDataChannel(in ACString label, in ACString protocol,
in unsigned short type, in boolean outOfOrderAllowed,
- in unsigned short maxTime, in unsigned short maxNum);
+ in unsigned short maxTime, in unsigned short maxNum,
+ in boolean externalNegotiated, in unsigned short stream);
void connectDataConnection(in unsigned short localport,
in unsigned short remoteport, in unsigned short numstreams);
};
--- a/dom/media/tests/mochitest/test_peerConnection_bug834153.html
+++ b/dom/media/tests/mochitest/test_peerConnection_bug834153.html
@@ -31,14 +31,14 @@
pc2.createAnswer(function (d) {
is(d.type,"answer","CreateAnswer created an answer");
SimpleTest.finish();
}, function (err) {
croak("createAnswer failed: " + err);
});
}, function (err) {
croak("createOffer failed: " + err);
- });
+ }, { mandatory: { OfferToReceiveAudio: true} });
}, true);
</script>
</pre>
</body>
</html>
--- a/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js
+++ b/dom/mobilemessage/src/ril/MobileMessageDatabaseService.js
@@ -1162,25 +1162,24 @@ MobileMessageDatabaseService.prototype =
threadParticipants = threadParticipants.concat(slicedReceivers);
}
}
let timestamp = aMessage.timestamp;
// Adding needed indexes and extra attributes for internal use.
// threadIdIndex & participantIdsIndex are filled in saveRecord().
- aMessage.deliveryIndex = [DELIVERY_RECEIVED, timestamp];
aMessage.readIndex = [FILTER_READ_UNREAD, timestamp];
+ aMessage.read = FILTER_READ_UNREAD;
if (aMessage.type == "sms") {
+ aMessage.delivery = DELIVERY_RECEIVED;
aMessage.deliveryStatus = DELIVERY_STATUS_SUCCESS;
- aMessage.delivery = DELIVERY_RECEIVED;
}
-
- aMessage.read = FILTER_READ_UNREAD;
+ aMessage.deliveryIndex = [aMessage.delivery, timestamp];
return this.saveRecord(aMessage, threadParticipants, aCallback);
},
saveSendingMessage: function saveSendingMessage(aMessage, aCallback) {
if ((aMessage.type != "sms" && aMessage.type != "mms") ||
(aMessage.type == "sms" && !aMessage.receiver) ||
(aMessage.type == "mms" && !Array.isArray(aMessage.receivers)) ||
--- a/dom/push/src/PushService.js
+++ b/dom/push/src/PushService.js
@@ -13,17 +13,16 @@ const Ci = Components.interfaces;
const Cu = Components.utils;
const Cr = Components.results;
Cu.import("resource://gre/modules/XPCOMUtils.jsm");
Cu.import("resource://gre/modules/Services.jsm");
Cu.import("resource://gre/modules/IndexedDBHelper.jsm");
Cu.import("resource://gre/modules/Timer.jsm");
Cu.import("resource://gre/modules/services-common/preferences.js");
-Cu.import("resource://gre/modules/services-common/utils.js");
Cu.import("resource://gre/modules/commonjs/sdk/core/promise.js");
const kPUSHDB_DB_NAME = "push";
const kPUSHDB_DB_VERSION = 1; // Change this if the IndexedDB format changes
const kPUSHDB_STORE_NAME = "push";
const kCONFLICT_RETRY_ATTEMPTS = 3; // If channelID registration says 409, how
// many times to retry with a new channelID
--- a/dom/quota/CheckQuotaHelper.cpp
+++ b/dom/quota/CheckQuotaHelper.cpp
@@ -30,39 +30,24 @@
USING_QUOTA_NAMESPACE
using namespace mozilla::services;
using mozilla::MutexAutoLock;
namespace {
inline
uint32_t
-GetQuotaPermissions(nsIDOMWindow* aWindow)
+GetQuotaPermissionFromWindow(nsIDOMWindow* aWindow)
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsCOMPtr<nsIScriptObjectPrincipal> sop(do_QueryInterface(aWindow));
NS_ENSURE_TRUE(sop, nsIPermissionManager::DENY_ACTION);
- if (nsContentUtils::IsSystemPrincipal(sop->GetPrincipal())) {
- return nsIPermissionManager::ALLOW_ACTION;
- }
-
- nsCOMPtr<nsIPermissionManager> permissionManager =
- do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
- NS_ENSURE_TRUE(permissionManager, nsIPermissionManager::DENY_ACTION);
-
- uint32_t permission;
- nsresult rv =
- permissionManager->TestPermissionFromPrincipal(sop->GetPrincipal(),
- PERMISSION_INDEXEDDB_UNLIMITED,
- &permission);
- NS_ENSURE_SUCCESS(rv, nsIPermissionManager::DENY_ACTION);
-
- return permission;
+ return CheckQuotaHelper::GetQuotaPermission(sop->GetPrincipal());
}
} // anonymous namespace
CheckQuotaHelper::CheckQuotaHelper(nsPIDOMWindow* aWindow,
mozilla::Mutex& aMutex)
: mWindow(aWindow),
mMutex(aMutex),
@@ -123,30 +108,54 @@ CheckQuotaHelper::Cancel()
}
else {
NS_WARNING("Failed to notify!");
}
}
}
}
+// static
+uint32_t
+CheckQuotaHelper::GetQuotaPermission(nsIPrincipal* aPrincipal)
+{
+ NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
+ NS_ASSERTION(aPrincipal, "Null principal!");
+
+ if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
+ return nsIPermissionManager::ALLOW_ACTION;
+ }
+
+ nsCOMPtr<nsIPermissionManager> pm =
+ do_GetService(NS_PERMISSIONMANAGER_CONTRACTID);
+ NS_ENSURE_TRUE(pm, nsIPermissionManager::DENY_ACTION);
+
+ uint32_t permission;
+ nsresult rv = pm->TestPermissionFromPrincipal(aPrincipal,
+ PERMISSION_INDEXEDDB_UNLIMITED,
+ &permission);
+ NS_ENSURE_SUCCESS(rv, nsIPermissionManager::DENY_ACTION);
+
+ return permission;
+}
+
NS_IMPL_THREADSAFE_ISUPPORTS3(CheckQuotaHelper, nsIRunnable,
nsIInterfaceRequestor,
nsIObserver)
NS_IMETHODIMP
CheckQuotaHelper::Run()
{
NS_ASSERTION(NS_IsMainThread(), "Wrong thread!");
nsresult rv = NS_OK;
if (NS_SUCCEEDED(rv)) {
if (!mHasPrompted) {
- mPromptResult = GetQuotaPermissions(mWindow);
+ mPromptResult = GetQuotaPermissionFromWindow(mWindow);
}
if (mHasPrompted) {
// Add permissions to the database, but only if we are in the parent
// process (if we are in the child process, we have already
// set the permission when the prompt was shown in the parent, as
// we cannot set the permission from the child).
if (mPromptResult != nsIPermissionManager::UNKNOWN_ACTION &&
--- a/dom/quota/CheckQuotaHelper.h
+++ b/dom/quota/CheckQuotaHelper.h
@@ -11,16 +11,17 @@
#include "nsIInterfaceRequestor.h"
#include "nsIObserver.h"
#include "nsIRunnable.h"
#include "mozilla/Mutex.h"
#include "mozilla/CondVar.h"
+class nsIPrincipal;
class nsPIDOMWindow;
BEGIN_QUOTA_NAMESPACE
class CheckQuotaHelper MOZ_FINAL : public nsIRunnable,
public nsIInterfaceRequestor,
public nsIObserver
{
@@ -32,16 +33,18 @@ public:
CheckQuotaHelper(nsPIDOMWindow* aWindow,
mozilla::Mutex& aMutex);
bool PromptAndReturnQuotaIsDisabled();
void Cancel();
+ static uint32_t GetQuotaPermission(nsIPrincipal* aPrincipal);
+
private:
nsPIDOMWindow* mWindow;
mozilla::Mutex& mMutex;
mozilla::CondVar mCondVar;
uint32_t mPromptResult;
bool mWaiting;
bool mHasPrompted;
--- a/dom/quota/QuotaManager.cpp
+++ b/dom/quota/QuotaManager.cpp
@@ -476,20 +476,24 @@ QuotaManager::Init()
// Register IndexedDB
mClients.AppendElement(new indexedDB::Client());
return NS_OK;
}
void
QuotaManager::InitQuotaForOrigin(const nsACString& aOrigin,
- int64_t aLimit,
- int64_t aUsage)
+ int64_t aLimitBytes,
+ int64_t aUsageBytes)
{
- OriginInfo* info = new OriginInfo(aOrigin, aLimit * 1024 * 1024, aUsage);
+ MOZ_ASSERT(aUsageBytes >= 0);
+ MOZ_ASSERT(aLimitBytes > 0);
+ MOZ_ASSERT(aUsageBytes <= aLimitBytes);
+
+ OriginInfo* info = new OriginInfo(aOrigin, aLimitBytes, aUsageBytes);
MutexAutoLock lock(mQuotaMutex);
NS_ASSERTION(!mOriginInfos.GetWeak(aOrigin), "Replacing an existing entry!");
mOriginInfos.Put(aOrigin, info);
}
void
@@ -813,17 +817,17 @@ QuotaManager::GetDirectoryForOrigin(cons
NS_ENSURE_SUCCESS(rv, rv);
directory.forget(aDirectory);
return NS_OK;
}
nsresult
QuotaManager::EnsureOriginIsInitialized(const nsACString& aOrigin,
- StoragePrivilege aPrivilege,
+ bool aTrackQuota,
nsIFile** aDirectory)
{
#ifdef DEBUG
{
bool correctThread;
NS_ASSERTION(NS_SUCCEEDED(mIOThread->IsOnCurrentThread(&correctThread)) &&
correctThread,
"Running on the wrong thread!");
@@ -865,17 +869,17 @@ QuotaManager::EnsureOriginIsInitialized(
}
rv = MaybeUpgradeOriginDirectory(directory);
NS_ENSURE_SUCCESS(rv, rv);
// We need to initialize directories of all clients if they exists and also
// get the total usage to initialize the quota.
nsAutoPtr<UsageRunnable> runnable;
- if (aPrivilege != Chrome) {
+ if (aTrackQuota) {
runnable = new UsageRunnable();
}
nsCOMPtr<nsISimpleEnumerator> entries;
rv = directory->GetDirectoryEntries(getter_AddRefs(entries));
NS_ENSURE_SUCCESS(rv, rv);
bool hasMore;
@@ -910,22 +914,35 @@ QuotaManager::EnsureOriginIsInitialized(
NS_WARNING("Unknown directory found!");
return NS_ERROR_UNEXPECTED;
}
rv = mClients[clientType]->InitOrigin(aOrigin, runnable);
NS_ENSURE_SUCCESS(rv, rv);
}
- if (aPrivilege != Chrome) {
- QuotaManager* quotaManager = QuotaManager::Get();
- NS_ASSERTION(quotaManager, "Shouldn't be null!");
+ if (aTrackQuota) {
+ uint64_t quotaMaxBytes = GetStorageQuotaMB() * 1024 * 1024;
+ uint64_t totalUsageBytes = runnable->TotalUsage();
+
+ if (totalUsageBytes > quotaMaxBytes) {
+ NS_WARNING("Origin is already using more storage than allowed by quota!");
+ return NS_ERROR_UNEXPECTED;
+ }
- quotaManager->InitQuotaForOrigin(aOrigin, GetStorageQuotaMB(),
- runnable->TotalUsage());
+ // XXX This signed/unsigned mismatch must be fixed.
+ int64_t limit = quotaMaxBytes >= uint64_t(INT64_MAX) ?
+ INT64_MAX :
+ int64_t(quotaMaxBytes);
+
+ int64_t usage = totalUsageBytes >= uint64_t(INT64_MAX) ?
+ INT64_MAX :
+ int64_t(totalUsageBytes);
+
+ InitQuotaForOrigin(aOrigin, limit, usage);
}
mInitializedOrigins.AppendElement(aOrigin);
NS_ADDREF(*aDirectory = directory);
return NS_OK;
}
--- a/dom/quota/QuotaManager.h
+++ b/dom/quota/QuotaManager.h
@@ -77,18 +77,18 @@ public:
static QuotaManager*
FactoryCreate();
// Returns true if we've begun the shutdown process.
static bool IsShuttingDown();
void
InitQuotaForOrigin(const nsACString& aOrigin,
- int64_t aLimit,
- int64_t aUsage);
+ int64_t aLimitBytes,
+ int64_t aUsageBytes);
void
DecreaseUsageForOrigin(const nsACString& aOrigin,
int64_t aSize);
void
RemoveQuotaForPattern(const nsACString& aPattern);
@@ -187,17 +187,17 @@ public:
}
nsresult
GetDirectoryForOrigin(const nsACString& aASCIIOrigin,
nsIFile** aDirectory) const;
nsresult
EnsureOriginIsInitialized(const nsACString& aOrigin,
- StoragePrivilege aPrivilege,
+ bool aTrackQuota,
nsIFile** aDirectory);
void
UninitializeOriginsByPattern(const nsACString& aPattern);
nsIThread*
IOThread()
{
--- a/dom/system/gonk/RILContentHelper.js
+++ b/dom/system/gonk/RILContentHelper.js
@@ -466,18 +466,18 @@ RILContentHelper.prototype = {
if (isNaN(parseInt(network.mcc, 10))) {
throw new Error("Invalid network MCC: " + network.mcc);
}
let request = Services.DOMRequest.createRequest(window);
let requestId = this.getRequestId(request);
- if (this.networkSelectionMode == RIL.GECKO_NETWORK_SELECTION_MANUAL
- && this.rilContext.voiceConnectionInfo.network === network) {
+ if (this.rilContext.networkSelectionMode == RIL.GECKO_NETWORK_SELECTION_MANUAL &&
+ this.rilContext.voiceConnectionInfo.network === network) {
// Already manually selected this network, so schedule
// onsuccess to be fired on the next tick
this.dispatchFireRequestSuccess(requestId, null);
return request;
}
this._selectingNetwork = network;
@@ -500,17 +500,17 @@ RILContentHelper.prototype = {
if (this._selectingNetwork) {
throw new Error("Already selecting a network: " + this._selectingNetwork);
}
let request = Services.DOMRequest.createRequest(window);
let requestId = this.getRequestId(request);
- if (this.networkSelectionMode == RIL.GECKO_NETWORK_SELECTION_AUTOMATIC) {
+ if (this.rilContext.networkSelectionMode == RIL.GECKO_NETWORK_SELECTION_AUTOMATIC) {
// Already using automatic selection mode, so schedule
// onsuccess to be be fired on the next tick
this.dispatchFireRequestSuccess(requestId, null);
return request;
}
this._selectingNetwork = "automatic";
cpmm.sendAsyncMessage("RIL:SelectNetworkAuto", {requestId: requestId});
@@ -1010,17 +1010,17 @@ RILContentHelper.prototype = {
break;
case "RIL:EnumerateCalls":
this.handleEnumerateCalls(msg.json.calls);
break;
case "RIL:GetAvailableNetworks":
this.handleGetAvailableNetworks(msg.json);
break;
case "RIL:NetworkSelectionModeChanged":
- this.networkSelectionMode = msg.json.mode;
+ this.rilContext.networkSelectionMode = msg.json.mode;
break;
case "RIL:SelectNetwork":
this.handleSelectNetwork(msg.json,
RIL.GECKO_NETWORK_SELECTION_MANUAL);
break;
case "RIL:SelectNetworkAuto":
this.handleSelectNetwork(msg.json,
RIL.GECKO_NETWORK_SELECTION_AUTOMATIC);
@@ -1165,17 +1165,17 @@ RILContentHelper.prototype = {
networks[i] = info;
}
Services.DOMRequest.fireSuccess(request, networks);
},
handleSelectNetwork: function handleSelectNetwork(message, mode) {
this._selectingNetwork = null;
- this.networkSelectionMode = mode;
+ this.rilContext.networkSelectionMode = mode;
if (message.errorMsg) {
this.fireRequestError(message.requestId, message.errorMsg);
} else {
this.fireRequestSuccess(message.requestId, null);
}
},
--- a/dom/webidl/SVGTextContentElement.webidl
+++ b/dom/webidl/SVGTextContentElement.webidl
@@ -30,11 +30,12 @@ interface SVGTextContentElement : SVGGra
SVGPoint getStartPositionOfChar(unsigned long charnum);
[Throws]
SVGPoint getEndPositionOfChar(unsigned long charnum);
[Creator, Throws]
SVGRect getExtentOfChar(unsigned long charnum);
[Throws]
float getRotationOfChar(unsigned long charnum);
long getCharNumAtPosition(SVGPoint point);
- // void selectSubString(unsigned long charnum, unsigned long nchars);
+ [Throws]
+ void selectSubString(unsigned long charnum, unsigned long nchars);
};
--- a/gfx/thebes/gfxWindowsPlatform.h
+++ b/gfx/thebes/gfxWindowsPlatform.h
@@ -152,16 +152,25 @@ public:
void VerifyD2DDevice(bool aAttemptForce);
#ifdef CAIRO_HAS_D2D_SURFACE
HRESULT CreateDevice(nsRefPtr<IDXGIAdapter1> &adapter1, int featureLevelIndex);
#endif
HDC GetScreenDC() { return mScreenDC; }
+ /**
+ * Return the resolution scaling factor to convert between "logical" or
+ * "screen" pixels as used by Windows (dependent on the DPI scaling option
+ * in the Display control panel) and actual device pixels.
+ */
+ double GetDPIScale() {
+ return GetDeviceCaps(mScreenDC, LOGPIXELSY) / 96.0;
+ }
+
nsresult GetFontList(nsIAtom *aLangGroup,
const nsACString& aGenericFamily,
nsTArray<nsString>& aListOfFonts);
nsresult UpdateFontList();
virtual void GetCommonFallbackFonts(const uint32_t aCh,
int32_t aRunScript,
--- a/image/test/reftest/pngsuite-ancillary/reftest.list
+++ b/image/test/reftest/pngsuite-ancillary/reftest.list
@@ -1,16 +1,16 @@
# PngSuite - Ancillary chunks
# cHRM chunks
#
# ccwn2c08 - gamma 1.0000 chunk, chroma chunk w:0.3127,0.3290 r:0.64,0.33 g:0.30,0.60 b:0.15,0.06
-fails-if(prefs.getIntPref("gfx.color_management.mode")!=2) == ccwn2c08.png ccwn2c08.html
+fails-if(prefs.getIntPref("gfx.color_management.mode")!=2) fuzzy-if(winWidget,8,569) == ccwn2c08.png ccwn2c08.html
# ccwn3p08 - gamma 1.0000 chunk, chroma chunk w:0.3127,0.3290 r:0.64,0.33 g:0.30,0.60 b:0.15,0.06
-fails-if(prefs.getIntPref("gfx.color_management.mode")!=2) == ccwn3p08.png ccwn3p08.html
+fails-if(prefs.getIntPref("gfx.color_management.mode")!=2) fuzzy-if(winWidget,8,577) == ccwn3p08.png ccwn3p08.html
# pHYs chunks
#
# PngSuite implies these first 3 should end up as 32x32 bitmaps, but
# per discussion in bug 408622 that's not actually true.
#
# cdfn2c08 - physical pixel dimensions, 8x32 flat pixels
fails-if(Android) == cdfn2c08.png cdfn2c08.html
new file mode 100644
--- /dev/null
+++ b/intl/hyphenation/src/README.mozilla
@@ -0,0 +1,28 @@
+About the hyphenation code in this directory
+============================================
+
+The core hyphenation code (files "hyphen.c" and "hyphen.h") comes from the
+Hyphen library, part of the hunspell project. The various COPYING* and README*
+files (except this README.mozilla) are likewise from the hunspell distribution
+of Hyphen:
+
+ http://sourceforge.net/projects/hunspell/files/Hyphen/.
+
+This code is distributed under the GPL 2.0/LGPL 2.1/MPL 1.1 tri-license, as
+detailed in the associated README and COPYING files.
+
+Note that we do not include other tools and resources found in the complete
+Hyphen package from upstream, so the original README.* files may refer to
+additional files that are not present in the Mozilla source tree.
+
+
+The other source files here:
+
+ hnjalloc.h
+ hnjstdio.cpp
+ nsHyphenationManager.cpp
+ nsHyphenator.cpp
+
+as well as the build files (Makefile.in and moz.build) are Mozilla-authored
+code, and the standard MPL 2.0 applies to these, as noted in the comments
+within the files.
--- a/intl/icu/source/aclocal.m4
+++ b/intl/icu/source/aclocal.m4
@@ -33,17 +33,20 @@ powerpc*-*-linux*)
*-*-linux*|*-*-gnu|*-*-k*bsd*-gnu|*-*-kopensolaris*-gnu) icu_cv_host_frag=mh-linux ;;
*-*-cygwin|*-*-mingw32)
if test "$GCC" = yes; then
AC_TRY_COMPILE([
#ifndef __MINGW32__
#error This is not MinGW
#endif], [], icu_cv_host_frag=mh-mingw, icu_cv_host_frag=mh-cygwin)
else
- icu_cv_host_frag=mh-cygwin-msvc
+ case "${host}" in
+ *-*-mingw32) icu_cv_host_frag=mh-msys-msvc ;;
+ *-*-cygwin) icu_cv_host_frag=mh-cygwin-msvc ;;
+ esac
fi ;;
*-*-*bsd*|*-*-dragonfly*) icu_cv_host_frag=mh-bsd-gcc ;;
*-*-aix*)
if test "$GCC" = yes; then
icu_cv_host_frag=mh-aix-gcc
else
icu_cv_host_frag=mh-aix-va
fi ;;
@@ -456,27 +459,31 @@ AC_DEFUN(AC_CHECK_STRICT_COMPILE,
# We use -std=c99 to disable the gnu99 defaults and its associated warnings
CFLAGS="$CFLAGS -Wall -std=c99 -pedantic -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings"
else
case "${host}" in
*-*-cygwin)
if test "`$CC /help 2>&1 | head -c9`" = "Microsoft"
then
CFLAGS="$CFLAGS /W4"
- fi
+ fi ;;
+ *-*-mingw32)
+ CFLAGS="$CFLAGS -W4" ;;
esac
fi
if test "$GXX" = yes
then
CXXFLAGS="$CXXFLAGS -W -Wall -pedantic -Wpointer-arith -Wwrite-strings -Wno-long-long"
else
case "${host}" in
*-*-cygwin)
if test "`$CXX /help 2>&1 | head -c9`" = "Microsoft"
then
CXXFLAGS="$CXXFLAGS /W4"
- fi
+ fi ;;
+ *-*-mingw32)
+ CXXFLAGS="$CXXFLAGS -W4" ;;
esac
fi
fi
])
copy from intl/icu/source/config/mh-cygwin-msvc
copy to intl/icu/source/config/mh-msys-msvc
--- a/intl/icu/source/config/mh-cygwin-msvc
+++ b/intl/icu/source/config/mh-msys-msvc
@@ -1,22 +1,22 @@
-## Cygwin with Microsoft Visual C++ compiler specific setup
+## MSYS with Microsoft Visual C++ compiler specific setup
## Copyright (c) 2001-2012, International Business Machines Corporation and
## others. All Rights Reserved.
# We install sbin tools into the same bin directory because
# pkgdata needs some of the tools in sbin, and we can't always depend on
# icu-config working on Windows.
sbindir=$(bindir)
## Commands to generate dependency files
GEN_DEPS.c= :
GEN_DEPS.cc= :
-#GEN_DEPS.c= $(COMPILE.c) /E
-#GEN_DEPS.cc= $(COMPILE.cc) /E
+#GEN_DEPS.c= $(COMPILE.c) -E
+#GEN_DEPS.cc= $(COMPILE.cc) -E
## Flags to create/use a static library
ifneq ($(ENABLE_SHARED),YES)
## Make sure that the static libraries can be built and used
CPPFLAGS += -DU_STATIC_IMPLEMENTATION#M#
else
## Make sure that the static libraries can be built
STATICCPPFLAGS = -DU_STATIC_IMPLEMENTATION
@@ -36,63 +36,63 @@ CPPFLAGS+=-DU_RELEASE=1#M#
endif
ifeq ($(ENABLE_DEBUG),1)
# Pass debugging flag through
CPPFLAGS+=-D_DEBUG=1#M#
ICULIBSUFFIX:=$(ICULIBSUFFIX)d#M#
endif
-# /GF pools strings and places them into read-only memory
-# /EHsc enables exception handling
-# /Zc:wchar_t makes wchar_t a native type. Required for C++ ABI compatibility.
+# -GF pools strings and places them into read-only memory
+# -EHsc enables exception handling
+# -Zc:wchar_t makes wchar_t a native type. Required for C++ ABI compatibility.
# -D_CRT_SECURE_NO_DEPRECATE is needed to quiet warnings about using standard C functions.
-CFLAGS+=/GF /nologo
-CXXFLAGS+=/GF /nologo /EHsc /Zc:wchar_t
+CFLAGS+=-GF -nologo
+CXXFLAGS+=-GF -nologo -EHsc -Zc:wchar_t
CPPFLAGS+=-D_CRT_SECURE_NO_DEPRECATE
DEFS+=-DWIN32 -DCYGWINMSVC
-LDFLAGS+=/nologo
+LDFLAGS+=-nologo
# Commands to compile
-COMPILE.c= $(CC) $(CPPFLAGS) $(DEFS) $(CFLAGS) /c
-COMPILE.cc= $(CXX) $(CPPFLAGS) $(DEFS) $(CXXFLAGS) /c
+COMPILE.c= $(CC) $(CPPFLAGS) $(DEFS) $(CFLAGS) -c
+COMPILE.cc= $(CXX) $(CPPFLAGS) $(DEFS) $(CXXFLAGS) -c
# Commands to link
-LINK.c= LINK.EXE /subsystem:console $(LDFLAGS)
-LINK.cc= LINK.EXE /subsystem:console $(LDFLAGS)
+LINK.c= LINK.EXE -subsystem:console $(LDFLAGS)
+LINK.cc= LINK.EXE -subsystem:console $(LDFLAGS)
## Commands to make a shared library
-SHLIB.c= LINK.EXE /DLL $(LDFLAGS)
-SHLIB.cc= LINK.EXE /DLL $(LDFLAGS)
+SHLIB.c= LINK.EXE -DLL $(LDFLAGS)
+SHLIB.cc= LINK.EXE -DLL $(LDFLAGS)
## Compiler switch to embed a runtime search path
LD_RPATH=
LD_RPATH_PRE=
## Compiler switch to embed a library name
-LD_SONAME = /IMPLIB:$(SO_TARGET:.dll=.lib)
+LD_SONAME = -IMPLIB:$(SO_TARGET:.dll=.lib)
## Shared object suffix
SO = dll
## Non-shared intermediate object suffix
STATIC_O = ao
# OUTOPT is for creating a specific output name
-OUTOPT = /out:
+OUTOPT = -out:
# Static library prefix and file extension
LIBSICU = $(STATIC_PREFIX)$(ICUPREFIX)
A = lib
# Cygwin's ar can't handle Win64 right now. So we use Microsoft's tool instead.
AR = LIB.EXE#M#
-ARFLAGS := /nologo $(ARFLAGS:r=)#M#
+ARFLAGS := -nologo $(ARFLAGS:r=)#M#
RANLIB = ls -s#M#
-AR_OUTOPT = /OUT:#M#
+AR_OUTOPT = -OUT:#M#
-## An import library is needed for z/OS, MSVC and Cygwin
+## An import library is needed for z-OS, MSVC and Cygwin
IMPORT_LIB_EXT = .lib
LIBPREFIX=
DEFAULT_LIBS = advapi32.lib
# Change the stubnames so that poorly working FAT disks and installation programs can work.
# This is also for backwards compatibility.
DATA_STUBNAME = dt
@@ -109,47 +109,35 @@ LIBICUUC= $(LIBDIR)/$(LIBICU)$(COMMON_ST
LIBICUI18N= $(LIBDIR)/$(LIBICU)$(I18N_STUBNAME)$(ICULIBSUFFIX).lib
LIBICULE= $(LIBDIR)/$(LIBICU)$(LAYOUT_STUBNAME)$(ICULIBSUFFIX).lib
LIBICULX= $(LIBDIR)/$(LIBICU)$(LAYOUTEX_STUBNAME)$(ICULIBSUFFIX).lib
LIBICUIO= $(LIBDIR)/$(LIBICU)$(IO_STUBNAME)$(ICULIBSUFFIX).lib
LIBCTESTFW= $(top_builddir)/tools/ctestfw/$(LIBICU)$(CTESTFW_STUBNAME)$(ICULIBSUFFIX).lib
LIBICUTOOLUTIL= $(LIBDIR)/$(LIBICU)$(TOOLUTIL_STUBNAME)$(ICULIBSUFFIX).lib
## These are the library specific LDFLAGS
-LDFLAGSICUDT+= /base:"0x4ad00000" /NOENTRY# The NOENTRY option is required for creating a resource-only DLL.
-LDFLAGSICUUC= /base:"0x4a800000"# in-uc = 1MB
-LDFLAGSICUI18N= /base:"0x4a900000"# io-in = 2MB
-LDFLAGSICUIO= /base:"0x4ab00000"# le-io = 1MB
-LDFLAGSICULE= /base:"0x4ac00000"# lx-le = 512KB
-LDFLAGSICULX= /base:"0x4ac80000"
+LDFLAGSICUDT+= -base:"0x4ad00000" -NOENTRY# The NOENTRY option is required for creating a resource-only DLL.
+LDFLAGSICUUC= -base:"0x4a800000"# in-uc = 1MB
+LDFLAGSICUI18N= -base:"0x4a900000"# io-in = 2MB
+LDFLAGSICUIO= -base:"0x4ab00000"# le-io = 1MB
+LDFLAGSICULE= -base:"0x4ac00000"# lx-le = 512KB
+LDFLAGSICULX= -base:"0x4ac80000"
LDFLAGSCTESTFW=# Unused for now.
-LDFLAGSICUTOOLUTIL= /base:"0x4ac00000"# Same as layout. Layout and tools probably won't mix.
-
-# The #M# is used to delete lines for icu-config
-# Current full path directory.
-CURR_FULL_DIR=$(subst \,/,$(shell cygpath -da .))#M# -m isn't used because it doesn't work on Win98
-# Current full path directory for use in source code in a -D compiler option.
-CURR_SRCCODE_FULL_DIR=$(subst \,\\,$(shell cygpath -da .))#M#
-
-ifeq ($(srcdir),.)
-SOURCE_FILE=$<
-else
-SOURCE_FILE=$(shell cygpath -dma $<)#M#
-endif
+LDFLAGSICUTOOLUTIL= -base:"0x4ac00000"# Same as layout. Layout and tools probably won't mix.
## Compilation rules
%.$(STATIC_O): $(srcdir)/%.c
- $(COMPILE.c) $(STATICCPPFLAGS) $(STATICCFLAGS) /Fo$@ $(SOURCE_FILE)
+ $(COMPILE.c) $(STATICCPPFLAGS) $(STATICCFLAGS) -Fo$@ $<
%.o: $(srcdir)/%.c
- $(COMPILE.c) $(DYNAMICCPPFLAGS) $(DYNAMICCFLAGS) /Fo$@ $(SOURCE_FILE)
+ $(COMPILE.c) $(DYNAMICCPPFLAGS) $(DYNAMICCFLAGS) -Fo$@ $<
%.$(STATIC_O): $(srcdir)/%.cpp
- $(COMPILE.cc) $(STATICCPPFLAGS) $(STATICCXXFLAGS) /Fo$@ $(SOURCE_FILE)
+ $(COMPILE.cc) $(STATICCPPFLAGS) $(STATICCXXFLAGS) -Fo$@ $<
%.o: $(srcdir)/%.cpp
- $(COMPILE.cc) $(DYNAMICCPPFLAGS) $(DYNAMICCXXFLAGS) /Fo$@ $(SOURCE_FILE)
+ $(COMPILE.cc) $(DYNAMICCPPFLAGS) $(DYNAMICCXXFLAGS) -Fo$@ $<
## Dependency rules
## This is a start to how depdendencies could work
# The commented out rules may not properly delete the file when ^C is pressed
# or the compiler fails.
# make currently doesn't like rules with C:\\PROGRA~1\\.. in the depedency.
# So system headers are ignored by ignoring \\
@@ -170,34 +158,34 @@ endif
# @echo -n "$@ $(basename $<).o : " > $@
# @$(SHELL) -ec '$(GEN_DEPS.cc) $< \
# | grep "#line 1 " | grep -v \\\\ | cut -d " " -f 3 \
# | /usr/bin/sort -u | sed s/\"$$/\\\\/ | sed s/^\"/\ / >> $@ \
# || (rm -f $@ && echo $@ && false)'
## Compile a Windows resource file
%.res : $(srcdir)/%.rc
- rc.exe /fo$@ $(CPPFLAGS) $(SOURCE_FILE)
+ rc.exe -fo$@ $(CPPFLAGS) $<
## Versioned target for a shared library.
FINAL_SO_TARGET= $(basename $(SO_TARGET))$(SO_TARGET_VERSION_MAJOR).$(SO)
MIDDLE_SO_TARGET=$(FINAL_SO_TARGET)
## Starting in MSVC 2005, manifest files are required. This reduces the obnoxiousness of this feature.
POST_SO_BUILD_STEP = @([ -e $<.manifest ] && \
( echo Embedding manifest into $< && mt.exe -nologo -manifest $<.manifest -outputresource:"$<;2" && rm -rf $<.manifest )) \
|| true
POST_BUILD_STEP = @([ -e $@.manifest ] && \
( echo Embedding manifest into $@ && mt.exe -nologo -manifest $@.manifest -outputresource:"$@;1" && rm -rf $@.manifest )) \
|| true
## Special pkgdata information that is needed
PKGDATA_VERSIONING = -r $(SO_TARGET_VERSION_MAJOR)
-ICUPKGDATA_INSTALL_DIR = $(shell mkdir -p $(DESTDIR)$(ICUPKGDATA_DIR) ; cygpath -dma $(DESTDIR)$(ICUPKGDATA_DIR))#M#
-ICUPKGDATA_INSTALL_LIBDIR = $(shell mkdir -p $(DESTDIR)$(libdir) ; cygpath -dma $(DESTDIR)$(libdir))#M#
+ICUPKGDATA_INSTALL_DIR = $(shell mkdir -p $(DESTDIR)$(ICUPKGDATA_DIR) ; echo $(DESTDIR)$(ICUPKGDATA_DIR))#M#
+ICUPKGDATA_INSTALL_LIBDIR = $(shell mkdir -p $(DESTDIR)$(libdir) ; echo $(DESTDIR)$(libdir))#M#
## Versioned import library names. The library names are versioned,
## but the import libraries do not need versioning.
IMPORT_LIB = $(basename $(SO_TARGET))$(IMPORT_LIB_EXT)#M#
MIDDLE_IMPORT_LIB = $(IMPORT_LIB)#M#
FINAL_IMPORT_LIB = $(MIDDLE_IMPORT_LIB)#M#
# The following is for Makefile.inc's use.
@@ -220,10 +208,10 @@ INSTALL-L=$(INSTALL_PROGRAM)
LDLIBRARYPATH_ENVVAR = PATH
# These are needed to allow the pkgdata nmake files to work
PKGDATA_INVOKE_OPTS = MAKEFLAGS=
# Include the version information in the shared library
ENABLE_SO_VERSION_DATA=1
-## End Cygwin-specific setup
+## End MSYS-specific setup
--- a/intl/icu/source/configure
+++ b/intl/icu/source/configure
@@ -4068,29 +4068,33 @@ fi
# We use -std=c99 to disable the gnu99 defaults and its associated warnings
CFLAGS="$CFLAGS -Wall -std=c99 -pedantic -Wshadow -Wpointer-arith -Wmissing-prototypes -Wwrite-strings"
else
case "${host}" in
*-*-cygwin)
if test "`$CC /help 2>&1 | head -c9`" = "Microsoft"
then
CFLAGS="$CFLAGS /W4"
- fi
+ fi ;;
+ *-*-mingw32)
+ CFLAGS="$CFLAGS -W4" ;;
esac
fi
if test "$GXX" = yes
then
CXXFLAGS="$CXXFLAGS -W -Wall -pedantic -Wpointer-arith -Wwrite-strings -Wno-long-long"
else
case "${host}" in
*-*-cygwin)
if test "`$CXX /help 2>&1 | head -c9`" = "Microsoft"
then
CXXFLAGS="$CXXFLAGS /W4"
- fi
+ fi ;;
+ *-*-mingw32)
+ CXXFLAGS="$CXXFLAGS -W4" ;;
esac
fi
fi
# Check if we can build and use 64-bit libraries
@@ -4864,17 +4868,20 @@ main ()
_ACEOF
if ac_fn_c_try_compile "$LINENO"; then :
icu_cv_host_frag=mh-mingw
else
icu_cv_host_frag=mh-cygwin
fi
rm -f core conftest.err conftest.$ac_objext conftest.$ac_ext
else
- icu_cv_host_frag=mh-cygwin-msvc
+ case "${host}" in
+ *-*-mingw32) icu_cv_host_frag=mh-msys-msvc ;;
+ *-*-cygwin) icu_cv_host_frag=mh-cygwin-msvc ;;
+ esac
fi ;;
*-*-*bsd*|*-*-dragonfly*) icu_cv_host_frag=mh-bsd-gcc ;;
*-*-aix*)
if test "$GCC" = yes; then
icu_cv_host_frag=mh-aix-gcc
else
icu_cv_host_frag=mh-aix-va
fi ;;
@@ -5106,17 +5113,17 @@ fi
{ $as_echo "$as_me:${as_lineno-$LINENO}: result: $enabled" >&5
$as_echo "$enabled" >&6; }
# MSVC floating-point option
MSVC_RELEASE_FLAG=""
if test $enabled = yes
then
- if test $icu_cv_host_frag = mh-cygwin-msvc
+ if test $icu_cv_host_frag = mh-cygwin-msvc -o $icu_cv_host_frag = mh-msys-msvc
then
cat confdefs.h - <<_ACEOF >conftest.$ac_ext
/* end confdefs.h. */
#if defined _MSC_VER && _MSC_VER >= 1400
#else
Microsoft Visual C++ < 2005
#endif
--- a/intl/icu/source/configure.in
+++ b/intl/icu/source/configure.in
@@ -313,18 +313,18 @@ AC_ARG_ENABLE(auto-cleanup,
esac],
)
AC_MSG_RESULT($enabled)
AC_SUBST(UCLN_NO_AUTO_CLEANUP)
# MSVC floating-point option
MSVC_RELEASE_FLAG=""
if test $enabled = yes
-then
- if test $icu_cv_host_frag = mh-cygwin-msvc
+then
+ if test $icu_cv_host_frag = mh-cygwin-msvc -o $icu_cv_host_frag = mh-msys-msvc
then
AC_COMPILE_IFELSE([AC_LANG_PROGRAM([[
#if defined _MSC_VER && _MSC_VER >= 1400
#else
Microsoft Visual C++ < 2005
#endif
]], [[]])],[MSVC_RELEASE_FLAG="/fp:precise"],[MSVC_RELEASE_FLAG="/Op"])
--- a/intl/icu/source/data/Makefile.in
+++ b/intl/icu/source/data/Makefile.in
@@ -345,17 +345,18 @@ GENRBOPTS=-k
-include $(MISCSRCDIR)/misclocal.mk
MSC_SOURCE= $(MISC_SOURCE) $(MISC_SOURCE_LOCAL)
MSC_SRC_FILES=$(MSC_SOURCE:%=$(MISCSRCDIR)/%)
ifeq ($(ENABLE_SO_VERSION_DATA),1)
ifeq ($(PKGDATA_MODE),dll)
SO_VERSION_DATA = $(OUTTMPDIR)/icudata.res
$(SO_VERSION_DATA) : $(MISCSRCDIR)/icudata.rc
- rc.exe /i$(srcdir)/../common /i$(top_builddir)/common /fo$@ $(CPPFLAGS) $(SOURCE_FILE)
+ # fixme: need to tell whether to use - or /, $(SOURCEFILE) or $<
+ rc.exe -i$(srcdir)/../common -i$(top_builddir)/common -fo$@ $(CPPFLAGS) $<
endif
endif
INDEX_NAME=res_index
INDEX_FILE=$(OUTTMPDIR)/$(INDEX_NAME).txt
ALL_RES_SRC= $(RES_SRC) $(TRNS_SOURCE) $(MSC_SOURCE)
RES_FILES = $(ALL_RES_SRC:%.txt=$(BUILDDIR)/%.res) $(BUILDDIR)/$(INDEX_NAME).res $(BUILDDIR)/pool.res
--- a/intl/icu/source/runConfigureICU
+++ b/intl/icu/source/runConfigureICU
@@ -279,16 +279,27 @@ case $platform in
DEBUG_CXXFLAGS='-g -O0'
;;
MinGW)
THE_OS="MinGW"
THE_COMP="the GNU C++"
RELEASE_CFLAGS='-O3'
RELEASE_CXXFLAGS='-O3'
;;
+ MSYS/MSVC)
+ THE_OS="MSYS"
+ THE_COMP="Microsoft Visual C++"
+ CC=cl; export CC
+ CXX=cl; export CXX
+ RELEASE_CFLAGS='-Gy -MD'
+ RELEASE_CXXFLAGS='-Gy -MD'
+ DEBUG_CFLAGS='-Zi -MDd'
+ DEBUG_CXXFLAGS='-Zi -MDd'
+ DEBUG_LDFLAGS='-DEBUG'
+ ;;
*BSD)
THE_OS="BSD"
THE_COMP="the GNU C++"
CC=gcc; export CC
CXX=g++; export CXX
DEBUG_CFLAGS='-g -O0'
DEBUG_CXFLAGS='-g -O0'
;;
--- a/intl/update-icu.sh
+++ b/intl/update-icu.sh
@@ -1,13 +1,24 @@
#!/bin/sh
# 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/.
+# Warning
+# =======
+# As of ICU 51.1, ICU as obtained from the ICU repository does not
+# build with the Mozilla build tools for Windows. Check
+# http://bugs.icu-project.org/trac/ticket/9985
+# whether this has been addressed in the version you're updating to.
+# If not, obtain the patch "Make ICU build with Mozilla build for Windows" from
+# https://bugzilla.mozilla.org/show_bug.cgi?id=724533
+# and reapply it after running update-icu.sh (additional updates may be needed).
+# If the bug has been addressed, please delete this warning.
+
# Usage: update-icu.sh <URL of ICU SVN with release>
# E.g., for ICU 50.1.1: update-icu.sh http://source.icu-project.org/repos/icu/icu/tags/release-50-1-1/
if [ $# -lt 1 ]; then
echo "Usage: update-icu.sh <URL of ICU SVN with release>"
exit 1
fi
--- a/js/src/builtin/Profilers.cpp
+++ b/js/src/builtin/Profilers.cpp
@@ -194,162 +194,171 @@ JS_DumpProfile(const char *outfile, cons
return ok;
}
#ifdef MOZ_PROFILING
struct RequiredStringArg {
JSContext *mCx;
char *mBytes;
- RequiredStringArg(JSContext *cx, unsigned argc, jsval *vp, size_t argi, const char *caller)
+ RequiredStringArg(JSContext *cx, const CallArgs &args, size_t argi, const char *caller)
: mCx(cx), mBytes(NULL)
{
- if (argc <= argi) {
+ if (args.length() <= argi) {
JS_ReportError(cx, "%s: not enough arguments", caller);
- } else if (!JSVAL_IS_STRING(JS_ARGV(cx, vp)[argi])) {
+ } else if (!args[argi].isString()) {
JS_ReportError(cx, "%s: invalid arguments (string expected)", caller);
} else {
- mBytes = JS_EncodeString(cx, JSVAL_TO_STRING(JS_ARGV(cx, vp)[argi]));
+ mBytes = JS_EncodeString(cx, args[argi].toString());
}
}
operator void*() {
return (void*) mBytes;
}
~RequiredStringArg() {
if (mBytes)
js_free(mBytes);
}
};
static JSBool
StartProfiling(JSContext *cx, unsigned argc, jsval *vp)
{
- if (argc == 0) {
- JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(NULL)));
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() == 0) {
+ args.rval().setBoolean(JS_StartProfiling(NULL));
return JS_TRUE;
}
- RequiredStringArg profileName(cx, argc, vp, 0, "startProfiling");
+ RequiredStringArg profileName(cx, args, 0, "startProfiling");
if (!profileName)
return JS_FALSE;
- JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StartProfiling(profileName.mBytes)));
+ args.rval().setBoolean(JS_StartProfiling(profileName.mBytes));
return JS_TRUE;
}
static JSBool
StopProfiling(JSContext *cx, unsigned argc, jsval *vp)
{
- if (argc == 0) {
- JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(NULL)));
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() == 0) {
+ args.rval().setBoolean(JS_StopProfiling(NULL));
return JS_TRUE;
}
- RequiredStringArg profileName(cx, argc, vp, 0, "stopProfiling");
+ RequiredStringArg profileName(cx, args, 0, "stopProfiling");
if (!profileName)
return JS_FALSE;
- JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_StopProfiling(profileName.mBytes)));
+ args.rval().setBoolean(JS_StopProfiling(profileName.mBytes));
return JS_TRUE;
}
static JSBool
PauseProfilers(JSContext *cx, unsigned argc, jsval *vp)
{
- if (argc == 0) {
- JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(NULL)));
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() == 0) {
+ args.rval().setBoolean(JS_PauseProfilers(NULL));
return JS_TRUE;
}
- RequiredStringArg profileName(cx, argc, vp, 0, "pauseProfiling");
+ RequiredStringArg profileName(cx, args, 0, "pauseProfiling");
if (!profileName)
return JS_FALSE;
- JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_PauseProfilers(profileName.mBytes)));
+ args.rval().setBoolean(JS_PauseProfilers(profileName.mBytes));
return JS_TRUE;
}
static JSBool
ResumeProfilers(JSContext *cx, unsigned argc, jsval *vp)
{
- if (argc == 0) {
- JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(NULL)));
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() == 0) {
+ args.rval().setBoolean(JS_ResumeProfilers(NULL));
return JS_TRUE;
}
- RequiredStringArg profileName(cx, argc, vp, 0, "resumeProfiling");
+ RequiredStringArg profileName(cx, args, 0, "resumeProfiling");
if (!profileName)
return JS_FALSE;
- JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(JS_ResumeProfilers(profileName.mBytes)));
+ args.rval().setBoolean(JS_ResumeProfilers(profileName.mBytes));
return JS_TRUE;
}
/* Usage: DumpProfile([filename[, profileName]]) */
static JSBool
DumpProfile(JSContext *cx, unsigned argc, jsval *vp)
{
bool ret;
- if (argc == 0) {
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() == 0) {
ret = JS_DumpProfile(NULL, NULL);
} else {
- RequiredStringArg filename(cx, argc, vp, 0, "dumpProfile");
+ RequiredStringArg filename(cx, args, 0, "dumpProfile");
if (!filename)
return JS_FALSE;
- if (argc == 1) {
+ if (args.length() == 1) {
ret = JS_DumpProfile(filename.mBytes, NULL);
} else {
- RequiredStringArg profileName(cx, argc, vp, 1, "dumpProfile");
+ RequiredStringArg profileName(cx, args, 1, "dumpProfile");
if (!profileName)
return JS_FALSE;
ret = JS_DumpProfile(filename.mBytes, profileName.mBytes);
}
}
- JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(ret));
+ args.rval().setBoolean(ret);
return true;
}
#if defined(MOZ_SHARK) || defined(MOZ_INSTRUMENTS)
static JSBool
IgnoreAndReturnTrue(JSContext *cx, unsigned argc, jsval *vp)
{
- JS_SET_RVAL(cx, vp, JSVAL_TRUE);
+ CallArgs args = CallArgsFromVp(argc, vp);
+ args.rval().setBoolean(true);
return true;
}
#endif
#ifdef MOZ_CALLGRIND
static JSBool
StartCallgrind(JSContext *cx, unsigned argc, jsval *vp)
{
- JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StartCallgrind()));
+ CallArgs args = CallArgsFromVp(argc, vp);
+ args.rval().setBoolean(js_StartCallgrind());
return JS_TRUE;
}
static JSBool
StopCallgrind(JSContext *cx, unsigned argc, jsval *vp)
{
- JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_StopCallgrind()));
+ CallArgs args = CallArgsFromVp(argc, vp);
+ args.rval().setBoolean(js_StopCallgrind());
return JS_TRUE;
}
static JSBool
DumpCallgrind(JSContext *cx, unsigned argc, jsval *vp)
{
- if (argc == 0) {
- JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(NULL)));
+ CallArgs args = CallArgsFromVp(argc, vp);
+ if (args.length() == 0) {
+ args.rval().setBoolean(js_DumpCallgrind(NULL));
return JS_TRUE;
}
- RequiredStringArg outFile(cx, argc, vp, 0, "dumpCallgrind");
+ RequiredStringArg outFile(cx, args, 0, "dumpCallgrind");
if (!outFile)
return JS_FALSE;
- JS_SET_RVAL(cx, vp, BOOLEAN_TO_JSVAL(js_DumpCallgrind(outFile.mBytes)));
+ args.rval().setBoolean(js_DumpCallgrind(outFile.mBytes));
return JS_TRUE;
}
#endif
static JSFunctionSpec profiling_functions[] = {
JS_FN("startProfiling", StartProfiling, 1,0),
JS_FN("stopProfiling", StopProfiling, 1,0),
JS_FN("pauseProfilers", PauseProfilers, 1,0),
--- a/layout/base/nsPresArena.h
+++ b/layout/base/nsPresArena.h
@@ -40,16 +40,17 @@ public:
// Every aID must always be used with the same object size, aSize.
NS_HIDDEN_(void*) AllocateByFrameID(nsQueryFrame::FrameIID aID, size_t aSize);
NS_HIDDEN_(void) FreeByFrameID(nsQueryFrame::FrameIID aID, void* aPtr);
enum ObjectID {
nsLineBox_id = nsQueryFrame::NON_FRAME_MARKER,
nsRuleNode_id,
nsStyleContext_id,
+ nsFrameList_id,
// The PresArena implementation uses this bit to distinguish objects
// allocated by size from objects allocated by type ID (that is, frames
// using AllocateByFrameID and other objects using AllocateByObjectID).
// It should not collide with any Object ID (above) or frame ID (in
// nsQueryFrame.h). It is not 0x80000000 to avoid the question of
// whether enumeration constants are signed.
NON_OBJECT_MARKER = 0x40000000
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -47,17 +47,16 @@
#include "nsTextFrameTextRunCache.h"
#include "nsCCUncollectableMarker.h"
#include "nsTextFragment.h"
#include "nsCSSRuleProcessor.h"
#include "nsCrossSiteListenerProxy.h"
#include "nsHTMLDNSPrefetch.h"
#include "nsHtml5Module.h"
#include "nsFocusManager.h"
-#include "nsFrameList.h"
#include "nsListControlFrame.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "SVGElementFactory.h"
#include "nsSVGUtils.h"
#include "nsMathMLAtoms.h"
#include "nsMathMLOperators.h"
#include "Navigator.h"
#include "nsDOMStorageBaseDB.h"
@@ -250,18 +249,16 @@ nsLayoutStatics::Initialize()
nsContentSink::InitializeStatics();
nsHtml5Module::InitializeStatics();
nsLayoutUtils::Initialize();
nsIPresShell::InitializeStatics();
nsRefreshDriver::InitializeStatics();
nsCORSListenerProxy::Startup();
- nsFrameList::Init();
-
NS_SealStaticAtomTable();
nsWindowMemoryReporter::Init();
SVGElementFactory::Init();
nsSVGUtils::Init();
InitProcessPriorityManager();
@@ -368,18 +365,16 @@ nsLayoutStatics::Shutdown()
nsTreeSanitizer::ReleaseStatics();
nsHtml5Module::ReleaseStatics();
nsRegion::ShutdownStatic();
NS_ShutdownEventTargetChainItemRecyclePool();
- nsFrameList::Shutdown();
-
HTMLInputElement::DestroyUploadLastDir();
nsLayoutUtils::Shutdown();
nsHyphenationManager::Shutdown();
nsEditorSpellCheck::ShutDown();
nsDOMMutationObserver::Shutdown();
--- a/layout/generic/nsAbsoluteContainingBlock.cpp
+++ b/layout/generic/nsAbsoluteContainingBlock.cpp
@@ -3,48 +3,63 @@
* 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/. */
/*
* code for managing absolutely positioned children of a rendering
* object that is a containing block for them
*/
-#include "nsCOMPtr.h"
#include "nsAbsoluteContainingBlock.h"
+
#include "nsContainerFrame.h"
+#include "nsGkAtoms.h"
#include "nsIPresShell.h"
#include "nsHTMLParts.h"
+#include "nsHTMLReflowState.h"
#include "nsPresContext.h"
#include "nsFrameManager.h"
#include "nsCSSFrameConstructor.h"
#ifdef DEBUG
#include "nsBlockFrame.h"
+
+static void PrettyUC(nscoord aSize, char* aBuf)
+{
+ if (NS_UNCONSTRAINEDSIZE == aSize) {
+ strcpy(aBuf, "UC");
+ } else {
+ if((int32_t)0xdeadbeef == aSize) {
+ strcpy(aBuf, "deadbeef");
+ } else {
+ sprintf(aBuf, "%d", aSize);
+ }
+ }
+}
#endif
nsresult
nsAbsoluteContainingBlock::SetInitialChildList(nsIFrame* aDelegatingFrame,
ChildListID aListID,
nsFrameList& aChildList)
{
- NS_PRECONDITION(GetChildListID() == aListID, "unexpected child list name");
+ NS_PRECONDITION(mChildListID == aListID, "unexpected child list name");
#ifdef DEBUG
nsFrame::VerifyDirtyBitSet(aChildList);
#endif
mAbsoluteFrames.SetFrames(aChildList);
return NS_OK;
}
nsresult
nsAbsoluteContainingBlock::AppendFrames(nsIFrame* aDelegatingFrame,
ChildListID aListID,
nsFrameList& aFrameList)
{
- NS_ASSERTION(GetChildListID() == aListID, "unexpected child list");
+ NS_ASSERTION(mChildListID == aListID, "unexpected child list");
// Append the frames to our list of absolutely positioned frames
#ifdef DEBUG
nsFrame::VerifyDirtyBitSet(aFrameList);
#endif
mAbsoluteFrames.AppendFrames(nullptr, aFrameList);
// no damage to intrinsic widths, since absolutely positioned frames can't
@@ -57,17 +72,17 @@ nsAbsoluteContainingBlock::AppendFrames(
}
nsresult
nsAbsoluteContainingBlock::InsertFrames(nsIFrame* aDelegatingFrame,
ChildListID aListID,
nsIFrame* aPrevFrame,
nsFrameList& aFrameList)
{
- NS_ASSERTION(GetChildListID() == aListID, "unexpected child list");
+ NS_ASSERTION(mChildListID == aListID, "unexpected child list");
NS_ASSERTION(!aPrevFrame || aPrevFrame->GetParent() == aDelegatingFrame,
"inserting after sibling frame with different parent");
#ifdef DEBUG
nsFrame::VerifyDirtyBitSet(aFrameList);
#endif
mAbsoluteFrames.InsertFrames(nullptr, aPrevFrame, aFrameList);
@@ -80,17 +95,17 @@ nsAbsoluteContainingBlock::InsertFrames(
return NS_OK;
}
void
nsAbsoluteContainingBlock::RemoveFrame(nsIFrame* aDelegatingFrame,
ChildListID aListID,
nsIFrame* aOldFrame)
{
- NS_ASSERTION(GetChildListID() == aListID, "unexpected child list");
+ NS_ASSERTION(mChildListID == aListID, "unexpected child list");
nsIFrame* nif = aOldFrame->GetNextInFlow();
if (nif) {
static_cast<nsContainerFrame*>(nif->GetParent())
->DeleteNextInFlowChild(aOldFrame->PresContext(), nif, false);
}
mAbsoluteFrames.DestroyFrame(aOldFrame);
}
@@ -323,17 +338,17 @@ nsAbsoluteContainingBlock::DoMarkFramesD
}
}
// XXX Optimize the case where it's a resize reflow and the absolutely
// positioned child has the exact same size and position and skip the
// reflow...
// When bug 154892 is checked in, make sure that when
-// GetChildListID() == kFixedList, the height is unconstrained.
+// mChildListID == kFixedList, the height is unconstrained.
// since we don't allow replicated frames to split.
nsresult
nsAbsoluteContainingBlock::ReflowAbsoluteFrame(nsIFrame* aDelegatingFrame,
nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nscoord aContainingBlockWidth,
nscoord aContainingBlockHeight,
@@ -477,27 +492,8 @@ nsAbsoluteContainingBlock::ReflowAbsolut
#endif
if (aOverflowAreas) {
aOverflowAreas->UnionWith(kidDesiredSize.mOverflowAreas + rect.TopLeft());
}
return rv;
}
-
-#ifdef DEBUG
- void nsAbsoluteContainingBlock::PrettyUC(nscoord aSize,
- char* aBuf)
-{
- if (NS_UNCONSTRAINEDSIZE == aSize) {
- strcpy(aBuf, "UC");
- }
- else {
- if((int32_t)0xdeadbeef == aSize)
- {
- strcpy(aBuf, "deadbeef");
- }
- else {
- sprintf(aBuf, "%d", aSize);
- }
- }
-}
-#endif
--- a/layout/generic/nsAbsoluteContainingBlock.h
+++ b/layout/generic/nsAbsoluteContainingBlock.h
@@ -7,62 +7,55 @@
* code for managing absolutely positioned children of a rendering
* object that is a containing block for them
*/
#ifndef nsAbsoluteContainingBlock_h___
#define nsAbsoluteContainingBlock_h___
#include "nsFrameList.h"
-#include "nsHTMLReflowState.h"
-#include "nsGkAtoms.h"
-#include "nsContainerFrame.h"
+#include "nsIFrame.h"
-class nsIAtom;
-class nsIFrame;
+class nsContainerFrame;
+class nsHTMLReflowState;
class nsPresContext;
/**
* This class contains the logic for being an absolute containing block. This
* class is used within viewport frames (for frames representing content with
* fixed position) and blocks (for frames representing absolutely positioned
* content), since each set of frames is absolutely positioned with respect to
* its parent.
*
* There is no principal child list, just a named child list which contains
* the absolutely positioned frames (kAbsoluteList or kFixedList).
*
* All functions include as the first argument the frame that is delegating
* the request.
- *
*/
class nsAbsoluteContainingBlock
{
public:
typedef nsIFrame::ChildListID ChildListID;
nsAbsoluteContainingBlock(ChildListID aChildListID)
#ifdef DEBUG
: mChildListID(aChildListID)
#endif
{
- NS_ASSERTION(mChildListID == nsIFrame::kAbsoluteList ||
- mChildListID == nsIFrame::kFixedList,
- "should either represent position:fixed or absolute content");
+ MOZ_ASSERT(mChildListID == nsIFrame::kAbsoluteList ||
+ mChildListID == nsIFrame::kFixedList,
+ "should either represent position:fixed or absolute content");
}
-#ifdef DEBUG
- ChildListID GetChildListID() const { return mChildListID; }
-#endif
-
const nsFrameList& GetChildList() const { return mAbsoluteFrames; }
void AppendChildList(nsTArray<nsIFrame::ChildList>* aLists,
ChildListID aListID) const
{
- NS_ASSERTION(aListID == GetChildListID(), "wrong list ID");
+ NS_ASSERTION(aListID == mChildListID, "wrong list ID");
GetChildList().AppendIfNonempty(aLists, aListID);
}
nsresult SetInitialChildList(nsIFrame* aDelegatingFrame,
ChildListID aListID,
nsFrameList& aChildList);
nsresult AppendFrames(nsIFrame* aDelegatingFrame,
ChildListID aListID,
@@ -70,80 +63,86 @@ public:
nsresult InsertFrames(nsIFrame* aDelegatingFrame,
ChildListID aListID,
nsIFrame* aPrevFrame,
nsFrameList& aFrameList);
void RemoveFrame(nsIFrame* aDelegatingFrame,
ChildListID aListID,
nsIFrame* aOldFrame);
- // Called by the delegating frame after it has done its reflow first. This
- // function will reflow any absolutely positioned child frames that need to
- // be reflowed, e.g., because the absolutely positioned child frame has
- // 'auto' for an offset, or a percentage based width or height.
- //
- // aOverflowAreas, if non-null, is unioned with (in the local
- // coordinate space) the overflow areas of the absolutely positioned
- // children.
- //
- // aReflowStatus is assumed to be already-initialized, e.g. with the status
- // of the delegating frame's main reflow. This function merges in the
- // statuses of the absolutely positioned children's reflows.
+ /**
+ * Called by the delegating frame after it has done its reflow first. This
+ * function will reflow any absolutely positioned child frames that need to
+ * be reflowed, e.g., because the absolutely positioned child frame has
+ * 'auto' for an offset, or a percentage based width or height.
+ *
+ * @param aOverflowAreas, if non-null, is unioned with (in the local
+ * coordinate space) the overflow areas of the absolutely positioned
+ * children.
+ *
+ * @param aReflowStatus is assumed to be already-initialized, e.g. with the
+ * status of the delegating frame's main reflow. This function merges in the
+ * statuses of the absolutely positioned children's reflows.
+ */
nsresult Reflow(nsContainerFrame* aDelegatingFrame,
nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nsReflowStatus& aReflowStatus,
nscoord aContainingBlockWidth,
nscoord aContainingBlockHeight,
bool aConstrainHeight,
bool aCBWidthChanged,
bool aCBHeightChanged,
nsOverflowAreas* aOverflowAreas);
void DestroyFrames(nsIFrame* aDelegatingFrame,
nsIFrame* aDestructRoot);
- bool HasAbsoluteFrames() {return mAbsoluteFrames.NotEmpty();}
+ bool HasAbsoluteFrames() const { return mAbsoluteFrames.NotEmpty(); }
- // Mark our size-dependent absolute frames with NS_FRAME_HAS_DIRTY_CHILDREN
- // so that we'll make sure to reflow them.
+ /**
+ * Mark our size-dependent absolute frames with NS_FRAME_HAS_DIRTY_CHILDREN
+ * so that we'll make sure to reflow them.
+ */
void MarkSizeDependentFramesDirty();
- // Mark all our absolute frames with NS_FRAME_IS_DIRTY
+ /**
+ * Mark all our absolute frames with NS_FRAME_IS_DIRTY.
+ */
void MarkAllFramesDirty();
protected:
- // Returns true if the position of f depends on the position of
- // its placeholder or if the position or size of f depends on a
- // containing block dimension that changed.
- bool FrameDependsOnContainer(nsIFrame* f, bool aCBWidthChanged,
- bool aCBHeightChanged);
+ /**
+ * Returns true if the position of aFrame depends on the position of
+ * its placeholder or if the position or size of aFrame depends on a
+ * containing block dimension that changed.
+ */
+ bool FrameDependsOnContainer(nsIFrame* aFrame, bool aCBWidthChanged,
+ bool aCBHeightChanged);
nsresult ReflowAbsoluteFrame(nsIFrame* aDelegatingFrame,
- nsPresContext* aPresContext,
+ nsPresContext* aPresContext,
const nsHTMLReflowState& aReflowState,
nscoord aContainingBlockWidth,
nscoord aContainingBlockHeight,
bool aConstrainHeight,
nsIFrame* aKidFrame,
nsReflowStatus& aStatus,
nsOverflowAreas* aOverflowAreas);
- // Mark our absolute frames dirty. If aMarkAllDirty is true, all will be
- // marked with NS_FRAME_IS_DIRTY. Otherwise, the size-dependant ones will be
- // marked with NS_FRAME_HAS_DIRTY_CHILDREN.
+ /**
+ * Mark our absolute frames dirty.
+ * @param aMarkAllDirty if true, all will be marked with NS_FRAME_IS_DIRTY.
+ * Otherwise, the size-dependant ones will be marked with
+ * NS_FRAME_HAS_DIRTY_CHILDREN.
+ */
void DoMarkFramesDirty(bool aMarkAllDirty);
protected:
nsFrameList mAbsoluteFrames; // additional named child list
#ifdef DEBUG
ChildListID const mChildListID; // kFixedList or kAbsoluteList
-
- // helper routine for debug printout
- void PrettyUC(nscoord aSize,
- char* aBuf);
#endif
};
#endif /* nsnsAbsoluteContainingBlock_h___ */
-
--- a/layout/generic/nsBlockFrame.cpp
+++ b/layout/generic/nsBlockFrame.cpp
@@ -70,16 +70,17 @@ static const int MIN_LINES_NEEDING_CURSO
static const PRUnichar kDiscCharacter = 0x2022;
static const PRUnichar kCircleCharacter = 0x25e6;
static const PRUnichar kSquareCharacter = 0x25aa;
#define DISABLE_FLOAT_BREAKING_IN_COLUMNS
using namespace mozilla;
using namespace mozilla::css;
+using namespace mozilla::layout;
#ifdef DEBUG
#include "nsBlockDebugFlags.h"
bool nsBlockFrame::gLamePaintMetrics;
bool nsBlockFrame::gLameReflowMetrics;
bool nsBlockFrame::gNoisy;
bool nsBlockFrame::gNoisyDamageRepair;
@@ -243,22 +244,19 @@ RecordReflowStatus(bool aChildIsBlock, n
// Destructor function for the overflowLines frame property
static void
DestroyOverflowLines(void* aPropertyValue)
{
NS_ERROR("Overflow lines should never be destroyed by the FramePropertyTable");
}
NS_DECLARE_FRAME_PROPERTY(OverflowLinesProperty, DestroyOverflowLines)
-NS_DECLARE_FRAME_PROPERTY(OverflowOutOfFlowsProperty,
- nsContainerFrame::DestroyFrameList)
-NS_DECLARE_FRAME_PROPERTY(PushedFloatProperty,
- nsContainerFrame::DestroyFrameList)
-NS_DECLARE_FRAME_PROPERTY(OutsideBulletProperty,
- nsContainerFrame::DestroyFrameList)
+NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowOutOfFlowsProperty)
+NS_DECLARE_FRAME_PROPERTY_FRAMELIST(PushedFloatProperty)
+NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OutsideBulletProperty)
NS_DECLARE_FRAME_PROPERTY(InsideBulletProperty, nullptr)
//----------------------------------------------------------------------
nsIFrame*
NS_NewBlockFrame(nsIPresShell* aPresShell, nsStyleContext* aContext, uint32_t aFlags)
{
nsBlockFrame* it = new (aPresShell) nsBlockFrame(aContext);
@@ -274,36 +272,46 @@ nsBlockFrame::~nsBlockFrame()
void
nsBlockFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
ClearLineCursor();
DestroyAbsoluteFrames(aDestructRoot);
mFloats.DestroyFramesFrom(aDestructRoot);
nsPresContext* presContext = PresContext();
+ nsIPresShell* shell = presContext->PresShell();
nsLineBox::DeleteLineList(presContext, mLines, aDestructRoot,
&mFrames);
- nsFrameList* pushedFloats = RemovePushedFloats();
- if (pushedFloats) {
- pushedFloats->DestroyFrom(aDestructRoot);
+ FramePropertyTable* props = presContext->PropertyTable();
+
+ if (HasPushedFloats()) {
+ SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+ PushedFloatProperty());
+ RemoveStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
}
// destroy overflow lines now
FrameLines* overflowLines = RemoveOverflowLines();
if (overflowLines) {
nsLineBox::DeleteLineList(presContext, overflowLines->mLines,
aDestructRoot, &overflowLines->mFrames);
delete overflowLines;
}
- {
- nsAutoOOFFrameList oofs(this);
- oofs.mList.DestroyFramesFrom(aDestructRoot);
- // oofs is now empty and will remove the frame list property
+ if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) {
+ SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+ OverflowOutOfFlowsProperty());
+ RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
+ }
+
+ if (HasOutsideBullet()) {
+ SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+ OutsideBulletProperty());
+ RemoveStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
}
nsBlockFrameSuper::DestroyFrom(aDestructRoot);
}
/* virtual */ nsILineIterator*
nsBlockFrame::GetLineIterator()
{
@@ -4468,30 +4476,30 @@ nsBlockFrame::DrainPushedFloats(nsBlockR
// placeholders were in earlier blocks (since first-in-flows whose
// placeholders are in this block will get pulled appropriately by
// AddFloat, and will then be more likely to be in the correct order).
// FIXME: What if there's a continuation in our pushed floats list
// whose prev-in-flow is in a previous continuation of this block
// rather than this block? Might we need to pull it back so we don't
// report ourselves complete?
// FIXME: Maybe we should just pull all of them back?
- nsFrameList *ourPushedFloats = GetPushedFloats();
+ nsPresContext* presContext = PresContext();
+ nsFrameList* ourPushedFloats = GetPushedFloats();
if (ourPushedFloats) {
// When we pull back floats, we want to put them with the pushed
// floats, which must live at the start of our float list, but we
// want them at the end of those pushed floats.
// FIXME: This isn't quite right! What if they're all pushed floats?
nsIFrame *insertionPrevSibling = nullptr; /* beginning of list */
for (nsIFrame* f = mFloats.FirstChild();
f && (f->GetStateBits() & NS_FRAME_IS_PUSHED_FLOAT);
f = f->GetNextSibling()) {
insertionPrevSibling = f;
}
- nsPresContext *presContext = PresContext();
for (nsIFrame *f = ourPushedFloats->LastChild(), *next; f; f = next) {
next = f->GetPrevSibling();
if (f->GetPrevContinuation()) {
// FIXME
} else {
nsPlaceholderFrame *placeholder =
presContext->FrameManager()->GetPlaceholderFrameFor(f);
@@ -4504,30 +4512,27 @@ nsBlockFrame::DrainPushedFloats(nsBlockR
// floats, but after other pushed floats.
ourPushedFloats->RemoveFrame(f);
mFloats.InsertFrame(nullptr, insertionPrevSibling, f);
}
}
}
if (ourPushedFloats->IsEmpty()) {
- delete RemovePushedFloats();
+ RemovePushedFloats()->Delete(presContext->PresShell());
}
}
// After our prev-in-flow has completed reflow, it may have a pushed
// floats list, containing floats that we need to own. Take these.
nsBlockFrame* prevBlock = static_cast<nsBlockFrame*>(GetPrevInFlow());
if (prevBlock) {
- nsFrameList *list = prevBlock->RemovePushedFloats();
- if (list) {
- if (list->NotEmpty()) {
- mFloats.InsertFrames(this, nullptr, *list);
- }
- delete list;
+ AutoFrameListPtr list(presContext, prevBlock->RemovePushedFloats());
+ if (list && list->NotEmpty()) {
+ mFloats.InsertFrames(this, nullptr, *list);
}
}
}
nsBlockFrame::FrameLines*
nsBlockFrame::GetOverflowLines() const
{
if (!HasOverflowLines()) {
@@ -4609,31 +4614,32 @@ nsBlockFrame::SetOverflowOutOfFlows(cons
{
NS_PRECONDITION(!!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) ==
!!aPropValue, "state does not match value");
if (aList.IsEmpty()) {
if (!(GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS)) {
return;
}
- nsFrameList* list =
- RemovePropTableFrames(PresContext(),
- OverflowOutOfFlowsProperty());
+ nsPresContext* pc = PresContext();
+ nsFrameList* list = RemovePropTableFrames(pc, OverflowOutOfFlowsProperty());
NS_ASSERTION(aPropValue == list, "prop value mismatch");
- delete list;
+ list->Clear();
+ list->Delete(pc->PresShell());
RemoveStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
}
else if (GetStateBits() & NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS) {
NS_ASSERTION(aPropValue == GetPropTableFrames(PresContext(),
OverflowOutOfFlowsProperty()),
"prop value mismatch");
*aPropValue = aList;
}
else {
- SetPropTableFrames(PresContext(), new nsFrameList(aList),
+ nsPresContext* pc = PresContext();
+ SetPropTableFrames(pc, new (pc->PresShell()) nsFrameList(aList),
OverflowOutOfFlowsProperty());
AddStateBits(NS_BLOCK_HAS_OVERFLOW_OUT_OF_FLOWS);
}
}
nsBulletFrame*
nsBlockFrame::GetInsideBullet() const
{
@@ -4685,17 +4691,17 @@ nsBlockFrame::GetPushedFloats() const
nsFrameList*
nsBlockFrame::EnsurePushedFloats()
{
nsFrameList *result = GetPushedFloats();
if (result)
return result;
- result = new nsFrameList;
+ result = new (PresContext()->PresShell()) nsFrameList;
Properties().Set(PushedFloatProperty(), result);
AddStateBits(NS_BLOCK_HAS_PUSHED_FLOATS);
return result;
}
nsFrameList*
nsBlockFrame::RemovePushedFloats()
@@ -6581,17 +6587,17 @@ nsBlockFrame::SetInitialChildList(ChildL
// it to the flow now.
if (NS_STYLE_LIST_STYLE_POSITION_INSIDE ==
styleList->mListStylePosition) {
nsFrameList bulletList(bullet, bullet);
AddFrames(bulletList, nullptr);
Properties().Set(InsideBulletProperty(), bullet);
AddStateBits(NS_BLOCK_FRAME_HAS_INSIDE_BULLET);
} else {
- nsFrameList* bulletList = new nsFrameList(bullet, bullet);
+ nsFrameList* bulletList = new (shell) nsFrameList(bullet, bullet);
Properties().Set(OutsideBulletProperty(), bulletList);
AddStateBits(NS_BLOCK_FRAME_HAS_OUTSIDE_BULLET);
}
}
}
return NS_OK;
}
--- a/layout/generic/nsBlockReflowState.cpp
+++ b/layout/generic/nsBlockReflowState.cpp
@@ -940,31 +940,40 @@ nsBlockReflowState::ClearFloats(nscoord
}
#endif
#ifdef NOISY_FLOAT_CLEARING
printf("nsBlockReflowState::ClearFloats: aY=%d breakType=%d\n",
aY, aBreakType);
mFloatManager->List(stdout);
#endif
-
+
+ if (!mFloatManager->HasAnyFloats()) {
+ return aY;
+ }
+
nscoord newY = aY;
if (aBreakType != NS_STYLE_CLEAR_NONE) {
newY = mFloatManager->ClearFloats(newY, aBreakType, aFlags);
}
if (aReplacedBlock) {
for (;;) {
nsFlowAreaRect floatAvailableSpace = GetFloatAvailableSpace(newY);
+ if (!floatAvailableSpace.mHasFloats) {
+ // If there aren't any floats here, then we always fit.
+ // We check this before calling WidthToClearPastFloats, which is
+ // somewhat expensive.
+ break;
+ }
nsBlockFrame::ReplacedElementWidthToClear replacedWidth =
nsBlockFrame::WidthToClearPastFloats(*this, floatAvailableSpace.mRect,
aReplacedBlock);
- if (!floatAvailableSpace.mHasFloats ||
- std::max(floatAvailableSpace.mRect.x - mContentArea.x,
+ if (std::max(floatAvailableSpace.mRect.x - mContentArea.x,
replacedWidth.marginLeft) +
replacedWidth.borderBoxWidth +
std::max(mContentArea.XMost() - floatAvailableSpace.mRect.XMost(),
replacedWidth.marginRight) <=
mContentArea.width) {
break;
}
// See the analogous code for inlines in nsBlockFrame::DoReflowInlineFrames
--- a/layout/generic/nsCanvasFrame.cpp
+++ b/layout/generic/nsCanvasFrame.cpp
@@ -22,20 +22,19 @@
#include "nsDisplayList.h"
#include "nsCSSFrameConstructor.h"
#include "nsFrameManager.h"
// for focus
#include "nsIScrollableFrame.h"
#include "nsIDocShell.h"
-#ifdef DEBUG_rods
//#define DEBUG_CANVAS_FOCUS
-#endif
+using namespace mozilla::layout;
nsIFrame*
NS_NewCanvasFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsCanvasFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsCanvasFrame)
@@ -425,17 +424,18 @@ nsCanvasFrame::Reflow(nsPresContext*
NS_FRAME_TRACE_REFLOW_IN("nsCanvasFrame::Reflow");
// Initialize OUT parameter
aStatus = NS_FRAME_COMPLETE;
nsCanvasFrame* prevCanvasFrame = static_cast<nsCanvasFrame*>
(GetPrevInFlow());
if (prevCanvasFrame) {
- nsAutoPtr<nsFrameList> overflow(prevCanvasFrame->StealOverflowFrames());
+ AutoFrameListPtr overflow(aPresContext,
+ prevCanvasFrame->StealOverflowFrames());
if (overflow) {
NS_ASSERTION(overflow->OnlyChild(),
"must have doc root as canvas frame's only child");
nsContainerFrame::ReparentFrameViewList(aPresContext, *overflow,
prevCanvasFrame, this);
// Prepend overflow to the our child list. There may already be
// children placeholders for fixed-pos elements, which don't get
// reflowed but must not be lost until the canvas frame is destroyed.
--- a/layout/generic/nsColumnSetFrame.cpp
+++ b/layout/generic/nsColumnSetFrame.cpp
@@ -16,16 +16,17 @@
#include "nsStyleConsts.h"
#include "nsCOMPtr.h"
#include "nsLayoutUtils.h"
#include "nsDisplayList.h"
#include "nsCSSRendering.h"
#include <algorithm>
using namespace mozilla;
+using namespace mozilla::layout;
class nsColumnSetFrame : public nsContainerFrame {
public:
NS_DECL_FRAMEARENA_HELPERS
nsColumnSetFrame(nsStyleContext* aContext);
NS_IMETHOD SetInitialChildList(ChildListID aListID,
@@ -860,30 +861,31 @@ nsColumnSetFrame::ReflowChildren(nsHTMLR
&& !NS_FRAME_IS_TRUNCATED(aStatus);
}
void
nsColumnSetFrame::DrainOverflowColumns()
{
// First grab the prev-in-flows overflows and reparent them to this
// frame.
+ nsPresContext* presContext = PresContext();
nsColumnSetFrame* prev = static_cast<nsColumnSetFrame*>(GetPrevInFlow());
if (prev) {
- nsAutoPtr<nsFrameList> overflows(prev->StealOverflowFrames());
+ AutoFrameListPtr overflows(presContext, prev->StealOverflowFrames());
if (overflows) {
- nsContainerFrame::ReparentFrameViewList(PresContext(), *overflows,
+ nsContainerFrame::ReparentFrameViewList(presContext, *overflows,
prev, this);
mFrames.InsertFrames(this, nullptr, *overflows);
}
}
// Now pull back our own overflows and append them to our children.
// We don't need to reparent them since we're already their parent.
- nsAutoPtr<nsFrameList> overflows(StealOverflowFrames());
+ AutoFrameListPtr overflows(presContext, StealOverflowFrames());
if (overflows) {
// We're already the parent for these frames, so no need to set
// their parent again.
mFrames.AppendFrames(nullptr, *overflows);
}
}
NS_IMETHODIMP
--- a/layout/generic/nsContainerFrame.cpp
+++ b/layout/generic/nsContainerFrame.cpp
@@ -43,16 +43,17 @@
#ifdef DEBUG
#undef NOISY
#else
#undef NOISY
#endif
using namespace mozilla;
using namespace mozilla::dom;
+using namespace mozilla::layout;
NS_IMPL_FRAMEARENA_HELPERS(nsContainerFrame)
nsContainerFrame::~nsContainerFrame()
{
}
NS_QUERYFRAME_HEAD(nsContainerFrame)
@@ -212,62 +213,65 @@ nsContainerFrame::DestroyAbsoluteFrames(
if (IsAbsoluteContainer()) {
GetAbsoluteContainingBlock()->DestroyFrames(this, aDestructRoot);
MarkAsNotAbsoluteContainingBlock();
}
}
void
nsContainerFrame::SafelyDestroyFrameListProp(nsIFrame* aDestructRoot,
+ nsIPresShell* aPresShell,
FramePropertyTable* aPropTable,
const FramePropertyDescriptor* aProp)
{
// Note that the last frame can be removed through another route and thus
// delete the property -- that's why we fetch the property again before
// removing each frame rather than fetching it once and iterating the list.
while (nsFrameList* frameList =
static_cast<nsFrameList*>(aPropTable->Get(this, aProp))) {
nsIFrame* frame = frameList->RemoveFirstChild();
if (MOZ_LIKELY(frame)) {
frame->DestroyFrom(aDestructRoot);
} else {
aPropTable->Remove(this, aProp);
- delete frameList;
+ frameList->Delete(aPresShell);
return;
}
}
}
void
nsContainerFrame::DestroyFrom(nsIFrame* aDestructRoot)
{
- // Prevent event dispatch during destruction
+ // Prevent event dispatch during destruction.
if (HasView()) {
GetView()->SetFrame(nullptr);
}
DestroyAbsoluteFrames(aDestructRoot);
- // Delete the primary child list
+ // Destroy frames on the principal child list.
mFrames.DestroyFramesFrom(aDestructRoot);
- // Destroy auxiliary frame lists
- nsPresContext* prescontext = PresContext();
-
- DestroyOverflowList(prescontext, aDestructRoot);
+ // Destroy frames on the auxiliary frame lists and delete the lists.
+ nsPresContext* pc = PresContext();
+ nsIPresShell* shell = pc->PresShell();
+ FramePropertyTable* props = pc->PropertyTable();
+ SafelyDestroyFrameListProp(aDestructRoot, shell, props, OverflowProperty());
- if (IsFrameOfType(nsIFrame::eCanContainOverflowContainers)) {
- FramePropertyTable* props = prescontext->PropertyTable();
- SafelyDestroyFrameListProp(aDestructRoot, props,
- OverflowContainersProperty());
- SafelyDestroyFrameListProp(aDestructRoot, props,
- ExcessOverflowContainersProperty());
- }
+ MOZ_ASSERT(IsFrameOfType(nsIFrame::eCanContainOverflowContainers) ||
+ !(props->Get(this, nsContainerFrame::OverflowContainersProperty()) ||
+ props->Get(this, nsContainerFrame::ExcessOverflowContainersProperty())),
+ "this type of frame should't have overflow containers");
- // Destroy the frame and remove the flow pointers
+ SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+ OverflowContainersProperty());
+ SafelyDestroyFrameListProp(aDestructRoot, shell, props,
+ ExcessOverflowContainersProperty());
+
nsSplittableFrame::DestroyFrom(aDestructRoot);
}
/////////////////////////////////////////////////////////////////////////////
// Child frame enumeration
const nsFrameList&
nsContainerFrame::GetChildList(ChildListID aListID) const
@@ -1107,17 +1111,17 @@ nsContainerFrame::ReflowOverflowContaine
// Our own excess overflow containers from a previous reflow can still be
// present if our next-in-flow hasn't been reflown yet.
nsFrameList* selfExcessOCFrames =
RemovePropTableFrames(aPresContext, ExcessOverflowContainersProperty());
if (selfExcessOCFrames) {
if (overflowContainers) {
overflowContainers->AppendFrames(nullptr, *selfExcessOCFrames);
- delete selfExcessOCFrames;
+ selfExcessOCFrames->Delete(aPresContext->PresShell());
} else {
overflowContainers = selfExcessOCFrames;
SetPropTableFrames(aPresContext, overflowContainers,
OverflowContainersProperty());
}
}
if (!overflowContainers) {
return NS_OK; // nothing to reflow
@@ -1225,17 +1229,17 @@ static bool
TryRemoveFrame(nsIFrame* aFrame, FramePropertyTable* aPropTable,
const FramePropertyDescriptor* aProp, nsIFrame* aChildToRemove)
{
nsFrameList* list = static_cast<nsFrameList*>(aPropTable->Get(aFrame, aProp));
if (list && list->StartRemoveFrame(aChildToRemove)) {
// aChildToRemove *may* have been removed from this list.
if (list->IsEmpty()) {
aPropTable->Remove(aFrame, aProp);
- delete list;
+ list->Delete(aFrame->PresContext()->PresShell());
}
return true;
}
return false;
}
nsresult
nsContainerFrame::StealFrame(nsPresContext* aPresContext,
@@ -1276,17 +1280,17 @@ nsContainerFrame::StealFrame(nsPresConte
removed = mFrames.StartRemoveFrame(aChild);
if (!removed) {
// We didn't find the child in our principal child list.
// Maybe it's on the overflow list?
nsFrameList* frameList = GetOverflowFrames();
if (frameList) {
removed = frameList->ContinueRemoveFrame(aChild);
if (frameList->IsEmpty()) {
- DestroyOverflowList(aPresContext, nullptr);
+ DestroyOverflowList(aPresContext);
}
}
}
}
NS_POSTCONDITION(removed, "StealFrame: can't find aChild");
return removed ? NS_OK : NS_ERROR_UNEXPECTED;
}
@@ -1323,30 +1327,16 @@ nsContainerFrame::StealFramesAfter(nsIFr
}
}
}
NS_ERROR("StealFramesAfter: can't find aChild");
return nsFrameList::EmptyList();
}
-void
-nsContainerFrame::DestroyOverflowList(nsPresContext* aPresContext,
- nsIFrame* aDestructRoot)
-{
- nsFrameList* list =
- RemovePropTableFrames(aPresContext, OverflowProperty());
- if (list) {
- if (aDestructRoot)
- list->DestroyFrom(aDestructRoot);
- else
- list->Destroy();
- }
-}
-
/*
* Create a next-in-flow for aFrame. Will return the newly created
* frame in aNextInFlowResult <b>if and only if</b> a new frame is
* created; otherwise nullptr is returned in aNextInFlowResult.
*/
nsresult
nsContainerFrame::CreateNextInFlow(nsPresContext* aPresContext,
nsIFrame* aFrame,
@@ -1429,17 +1419,17 @@ nsContainerFrame::DeleteNextInFlowChild(
/**
* Set the frames on the overflow list
*/
void
nsContainerFrame::SetOverflowFrames(nsPresContext* aPresContext,
const nsFrameList& aOverflowFrames)
{
NS_PRECONDITION(aOverflowFrames.NotEmpty(), "Shouldn't be called");
- nsFrameList* newList = new nsFrameList(aOverflowFrames);
+ nsFrameList* newList = new (aPresContext->PresShell()) nsFrameList(aOverflowFrames);
aPresContext->PropertyTable()->Set(this, OverflowProperty(), newList);
}
nsFrameList*
nsContainerFrame::GetPropTableFrames(nsPresContext* aPresContext,
const FramePropertyDescriptor* aProperty) const
{
@@ -1461,16 +1451,17 @@ nsContainerFrame::SetPropTableFrames(nsP
const FramePropertyDescriptor* aProperty)
{
NS_PRECONDITION(aPresContext && aProperty && aFrameList, "null ptr");
NS_PRECONDITION(
(aProperty != nsContainerFrame::OverflowContainersProperty() &&
aProperty != nsContainerFrame::ExcessOverflowContainersProperty()) ||
IsFrameOfType(nsIFrame::eCanContainOverflowContainers),
"this type of frame can't have overflow containers");
+ MOZ_ASSERT(!GetPropTableFrames(aPresContext, aProperty));
aPresContext->PropertyTable()->Set(this, aProperty, aFrameList);
}
/**
* Push aFromChild and its next siblings to the next-in-flow. Change the
* geometric parent of each frame that's pushed. If there is no next-in-flow
* the frames are placed on the overflow list (and the geometric parent is
* left unchanged).
@@ -1525,17 +1516,18 @@ nsContainerFrame::PushChildren(nsPresCon
bool
nsContainerFrame::MoveOverflowToChildList(nsPresContext* aPresContext)
{
bool result = false;
// Check for an overflow list with our prev-in-flow
nsContainerFrame* prevInFlow = (nsContainerFrame*)GetPrevInFlow();
if (nullptr != prevInFlow) {
- nsAutoPtr<nsFrameList> prevOverflowFrames(prevInFlow->StealOverflowFrames());
+ AutoFrameListPtr prevOverflowFrames(aPresContext,
+ prevInFlow->StealOverflowFrames());
if (prevOverflowFrames) {
// Tables are special; they can have repeated header/footer
// frames on mFrames at this point.
NS_ASSERTION(mFrames.IsEmpty() || GetType() == nsGkAtoms::tableFrame,
"bad overflow list");
// When pushing and pulling frames we need to check for whether any
// views need to be reparented.
nsContainerFrame::ReparentFrameViewList(aPresContext,
@@ -1548,17 +1540,17 @@ nsContainerFrame::MoveOverflowToChildLis
// It's also possible that we have an overflow list for ourselves.
return DrainSelfOverflowList() || result;
}
bool
nsContainerFrame::DrainSelfOverflowList()
{
- nsAutoPtr<nsFrameList> overflowFrames(StealOverflowFrames());
+ AutoFrameListPtr overflowFrames(PresContext(), StealOverflowFrames());
if (overflowFrames) {
NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
mFrames.AppendFrames(nullptr, *overflowFrames);
return true;
}
return false;
}
@@ -1691,17 +1683,17 @@ nsOverflowContinuationTracker::Insert(ns
rv = static_cast<nsContainerFrame*>(aOverflowCont->GetParent())
->StealFrame(presContext, aOverflowCont);
NS_ENSURE_SUCCESS(rv, rv);
}
else {
aOverflowCont->AddStateBits(NS_FRAME_IS_OVERFLOW_CONTAINER);
}
if (!mOverflowContList) {
- mOverflowContList = new nsFrameList();
+ mOverflowContList = new (presContext->PresShell()) nsFrameList();
mParent->SetPropTableFrames(presContext, mOverflowContList,
nsContainerFrame::ExcessOverflowContainersProperty());
SetUpListWalker();
}
if (aOverflowCont->GetParent() != mParent) {
nsContainerFrame::ReparentFrameView(presContext, aOverflowCont,
aOverflowCont->GetParent(),
mParent);
--- a/layout/generic/nsContainerFrame.h
+++ b/layout/generic/nsContainerFrame.h
@@ -7,17 +7,16 @@
#ifndef nsContainerFrame_h___
#define nsContainerFrame_h___
#include "mozilla/Attributes.h"
#include "nsSplittableFrame.h"
#include "nsFrameList.h"
#include "nsLayoutUtils.h"
-#include "nsAutoPtr.h"
// Option flags for ReflowChild() and FinishReflowChild()
// member functions
#define NS_FRAME_NO_MOVE_VIEW 0x0001
#define NS_FRAME_NO_MOVE_FRAME (0x0002 | NS_FRAME_NO_MOVE_VIEW)
#define NS_FRAME_NO_SIZE_VIEW 0x0004
#define NS_FRAME_NO_VISIBILITY 0x0008
// Only applies to ReflowChild; if true, don't delete the next-in-flow, even
@@ -353,27 +352,31 @@ public:
* paint the background/borders/outline of this frame. This should
* probably be avoided and eventually removed. It's currently here
* to emulate what nsContainerFrame::Paint did.
*/
virtual void BuildDisplayList(nsDisplayListBuilder* aBuilder,
const nsRect& aDirtyRect,
const nsDisplayListSet& aLists) MOZ_OVERRIDE;
- // Destructor function for the proptable-stored framelists
+ /**
+ * Destructor function for the proptable-stored framelists --
+ * it should never be called.
+ */
static void DestroyFrameList(void* aPropertyValue)
{
- if (aPropertyValue) {
- static_cast<nsFrameList*>(aPropertyValue)->Destroy();
- }
+ MOZ_ASSERT(false, "The owning frame should destroy its nsFrameList props");
}
- NS_DECLARE_FRAME_PROPERTY(OverflowProperty, DestroyFrameList)
- NS_DECLARE_FRAME_PROPERTY(OverflowContainersProperty, DestroyFrameList)
- NS_DECLARE_FRAME_PROPERTY(ExcessOverflowContainersProperty, DestroyFrameList)
+#define NS_DECLARE_FRAME_PROPERTY_FRAMELIST(prop) \
+ NS_DECLARE_FRAME_PROPERTY(prop, nsContainerFrame::DestroyFrameList)
+
+ NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowProperty)
+ NS_DECLARE_FRAME_PROPERTY_FRAMELIST(OverflowContainersProperty)
+ NS_DECLARE_FRAME_PROPERTY_FRAMELIST(ExcessOverflowContainersProperty)
protected:
nsContainerFrame(nsStyleContext* aContext) : nsSplittableFrame(aContext) {}
~nsContainerFrame();
/**
* Helper for DestroyFrom. DestroyAbsoluteFrames is called before
* destroying frames on lists that can contain placeholders.
@@ -422,36 +425,33 @@ protected:
* still owned by this frame. A non-null return value indicates that the
* list is nonempty.
*/
inline nsFrameList* GetOverflowFrames() const;
/**
* As GetOverflowFrames, but removes the overflow frames property. The
* caller is responsible for deleting nsFrameList and either passing
- * ownership of the frames to someone else or destroying the frames. A
- * non-null return value indicates that the list is nonempty. The
+ * ownership of the frames to someone else or destroying the frames.
+ * A non-null return value indicates that the list is nonempty. The
* recommended way to use this function it to assign its return value
- * into an nsAutoPtr.
+ * into an AutoFrameListPtr.
*/
inline nsFrameList* StealOverflowFrames();
/**
* Set the overflow list. aOverflowFrames must not be an empty list.
*/
void SetOverflowFrames(nsPresContext* aPresContext,
const nsFrameList& aOverflowFrames);
/**
- * Destroy the overflow list and any frames that are on it.
- * Calls DestructFrom() insead of Destruct() on the frames if
- * aDestructRoot is non-null.
+ * Destroy the overflow list, which must be empty.
*/
- void DestroyOverflowList(nsPresContext* aPresContext,
- nsIFrame* aDestructRoot);
+ inline void DestroyOverflowList(nsPresContext* aPresContext);
/**
* Moves any frames on both the prev-in-flow's overflow list and the
* receiver's overflow to the receiver's child list.
*
* Resets the overlist pointers to nullptr, and updates the receiver's child
* count and content mapping.
*
@@ -506,16 +506,17 @@ protected:
const FramePropertyDescriptor* aProperty);
/**
* Safely destroy the frames on the nsFrameList stored on aProp for this
* frame then remove the property and delete the frame list.
* Nothing happens if the property doesn't exist.
*/
void SafelyDestroyFrameListProp(nsIFrame* aDestructRoot,
+ nsIPresShell* aPresShell,
mozilla::FramePropertyTable* aPropTable,
const FramePropertyDescriptor* aProp);
// ==========================================================================
nsFrameList mFrames;
};
@@ -673,9 +674,17 @@ nsFrameList*
nsContainerFrame::StealOverflowFrames()
{
nsFrameList* list =
static_cast<nsFrameList*>(Properties().Remove(OverflowProperty()));
NS_ASSERTION(!list || !list->IsEmpty(), "Unexpected empty overflow list");
return list;
}
+inline void
+nsContainerFrame::DestroyOverflowList(nsPresContext* aPresContext)
+{
+ nsFrameList* list = RemovePropTableFrames(aPresContext, OverflowProperty());
+ MOZ_ASSERT(list && list->IsEmpty());
+ list->Delete(aPresContext->PresShell());
+}
+
#endif /* nsContainerFrame_h___ */
--- a/layout/generic/nsFirstLetterFrame.cpp
+++ b/layout/generic/nsFirstLetterFrame.cpp
@@ -13,17 +13,18 @@
#include "nsLineLayout.h"
#include "nsGkAtoms.h"
#include "nsAutoPtr.h"
#include "nsStyleSet.h"
#include "nsFrameManager.h"
#include "nsPlaceholderFrame.h"
#include "nsCSSFrameConstructor.h"
-using namespace::mozilla;
+using namespace mozilla;
+using namespace mozilla::layout;
nsIFrame*
NS_NewFirstLetterFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
{
return new (aPresShell) nsFirstLetterFrame(aContext);
}
NS_IMPL_FRAMEARENA_HELPERS(nsFirstLetterFrame)
@@ -340,35 +341,34 @@ nsFirstLetterFrame::CreateContinuationFo
*aContinuation = continuation;
return rv;
}
void
nsFirstLetterFrame::DrainOverflowFrames(nsPresContext* aPresContext)
{
- nsAutoPtr<nsFrameList> overflowFrames;
-
// Check for an overflow list with our prev-in-flow
nsFirstLetterFrame* prevInFlow = (nsFirstLetterFrame*)GetPrevInFlow();
- if (nullptr != prevInFlow) {
- overflowFrames = prevInFlow->StealOverflowFrames();
+ if (prevInFlow) {
+ AutoFrameListPtr overflowFrames(aPresContext,
+ prevInFlow->StealOverflowFrames());
if (overflowFrames) {
NS_ASSERTION(mFrames.IsEmpty(), "bad overflow list");
// When pushing and pulling frames we need to check for whether any
// views need to be reparented.
nsContainerFrame::ReparentFrameViewList(aPresContext, *overflowFrames,
prevInFlow, this);
mFrames.InsertFrames(this, nullptr, *overflowFrames);
}
}
// It's also possible that we have an overflow list for ourselves
- overflowFrames = StealOverflowFrames();
+ AutoFrameListPtr overflowFrames(aPresContext, StealOverflowFrames());
if (overflowFrames) {
NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
mFrames.AppendFrames(nullptr, *overflowFrames);
}
// Now repair our first frames style context (since we only reflow
// one frame there is no point in doing any other ones until they
// are reflowed)
--- a/layout/generic/nsFrameList.cpp
+++ b/layout/generic/nsFrameList.cpp
@@ -1,51 +1,47 @@
/* -*- Mode: C++; 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/. */
#include "nsFrameList.h"
#include "nsIFrame.h"
#include "nsLayoutUtils.h"
+#include "nsPresContext.h"
+#include "nsIPresShell.h"
#ifdef IBMBIDI
#include "nsCOMPtr.h"
#include "nsGkAtoms.h"
#include "nsILineIterator.h"
#include "nsBidiPresUtils.h"
#endif // IBMBIDI
-const nsFrameList* nsFrameList::sEmptyList;
+namespace mozilla {
+namespace layout {
+namespace detail {
+const AlignedFrameListBytes gEmptyFrameListBytes = { 0 };
+}
+}
+}
-/* static */
-void
-nsFrameList::Init()
+void*
+nsFrameList::operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW
{
- NS_PRECONDITION(!sEmptyList, "Shouldn't be allocated");
-
- sEmptyList = new nsFrameList();
+ return aPresShell->AllocateByObjectID(nsPresArena::nsFrameList_id, sz);
}
void
-nsFrameList::Destroy()
+nsFrameList::Delete(nsIPresShell* aPresShell)
{
- NS_PRECONDITION(this != sEmptyList, "Shouldn't Destroy() sEmptyList");
-
- DestroyFrames();
- delete this;
-}
+ NS_PRECONDITION(this != &EmptyList(), "Shouldn't Delete() this list");
+ NS_ASSERTION(IsEmpty(), "Shouldn't Delete() a non-empty list");
-void
-nsFrameList::DestroyFrom(nsIFrame* aDestructRoot)
-{
- NS_PRECONDITION(this != sEmptyList, "Shouldn't Destroy() sEmptyList");
-
- DestroyFramesFrom(aDestructRoot);
- delete this;
+ aPresShell->FreeByObjectID(nsPresArena::nsFrameList_id, this);
}
void
nsFrameList::DestroyFrames()
{
while (nsIFrame* frame = RemoveFirstChild()) {
frame->Destroy();
}
@@ -539,8 +535,21 @@ nsFrameList::VerifyList() const
NS_ASSERTION(mLastChild == nsLayoutUtils::GetLastSibling(mFirstChild),
"bogus mLastChild");
// XXX we should also assert that all GetParent() are either null or
// the same non-null value, but nsCSSFrameConstructor::nsFrameItems
// prevents that, e.g. table captions.
}
#endif
+
+namespace mozilla {
+namespace layout {
+
+AutoFrameListPtr::~AutoFrameListPtr()
+{
+ if (mFrameList) {
+ mFrameList->Delete(mPresContext->PresShell());
+ }
+}
+
+}
+}
--- a/layout/generic/nsFrameList.h
+++ b/layout/generic/nsFrameList.h
@@ -8,16 +8,19 @@
#include "nscore.h"
#include "nsTraceRefcnt.h"
#include <stdio.h> /* for FILE* */
#include "nsDebug.h"
#include "nsTArray.h"
class nsIFrame;
+class nsIPresShell;
+class nsPresContext;
+
namespace mozilla {
namespace layout {
class FrameChildList;
enum FrameChildListID {
// The individual concrete child lists.
kPrincipalList = 0x1,
kPopupList = 0x2,
kCaptionList = 0x4,
@@ -45,63 +48,52 @@ namespace layout {
/**
* A class for managing a list of frames.
*/
class nsFrameList {
public:
nsFrameList() :
mFirstChild(nullptr), mLastChild(nullptr)
{
- MOZ_COUNT_CTOR(nsFrameList);
}
nsFrameList(nsIFrame* aFirstFrame, nsIFrame* aLastFrame) :
mFirstChild(aFirstFrame), mLastChild(aLastFrame)
{
- MOZ_COUNT_CTOR(nsFrameList);
VerifyList();
}
nsFrameList(const nsFrameList& aOther) :
mFirstChild(aOther.mFirstChild), mLastChild(aOther.mLastChild)
{
- MOZ_COUNT_CTOR(nsFrameList);
}
- ~nsFrameList() {
- MOZ_COUNT_DTOR(nsFrameList);
- // Don't destroy our frames here, so that we can have temporary nsFrameLists
- }
+ /**
+ * Allocate a nsFrameList from the shell arena.
+ */
+ void* operator new(size_t sz, nsIPresShell* aPresShell) CPP_THROW_NEW;
+
+ /**
+ * Deallocate this list that was allocated from the shell arena.
+ * The list is required to be empty.
+ */
+ void Delete(nsIPresShell* aPresShell);
/**
* For each frame in this list: remove it from the list then call
* Destroy() on it.
*/
void DestroyFrames();
/**
* For each frame in this list: remove it from the list then call
- * DestroyFrom() on it.
+ * DestroyFrom(aDestructRoot) on it.
*/
void DestroyFramesFrom(nsIFrame* aDestructRoot);
- /**
- * For each frame in this list: remove it from the list then call
- * Destroy() on it. Finally <code>delete this</code>.
- *
- */
- void Destroy();
-
- /**
- * For each frame in this list: remove it from the list then call
- * DestroyFrom() on it. Finally <code>delete this</code>.
- *
- */
- void DestroyFrom(nsIFrame* aDestructRoot);
-
void Clear() { mFirstChild = mLastChild = nullptr; }
void SetFrames(nsIFrame* aFrameList);
void SetFrames(nsFrameList& aFrameList) {
NS_PRECONDITION(!mFirstChild, "Losing frames");
mFirstChild = aFrameList.FirstChild();
@@ -286,19 +278,17 @@ public:
*/
nsIFrame* GetNextVisualFor(nsIFrame* aFrame) const;
#endif // IBMBIDI
#ifdef DEBUG
void List(FILE* out) const;
#endif
- static void Init();
- static void Shutdown() { delete sEmptyList; }
- static const nsFrameList& EmptyList() { return *sEmptyList; }
+ static inline const nsFrameList& EmptyList();
class Enumerator;
/**
* A class representing a slice of a frame list.
*/
class Slice {
friend class Enumerator;
@@ -453,30 +443,66 @@ public:
nsIFrame* PrevFrame() const { return mPrev; }
nsIFrame* NextFrame() const { return mFrame; }
protected:
nsIFrame* mPrev;
};
private:
+ void operator delete(void*) MOZ_DELETE;
+
#ifdef DEBUG_FRAME_LIST
void VerifyList() const;
#else
void VerifyList() const {}
#endif
- static const nsFrameList* sEmptyList;
-
protected:
/**
* Disconnect aFrame from its siblings. This must only be called if aFrame
* is NOT the first or last sibling, because otherwise its nsFrameList will
* have a stale mFirst/LastChild pointer. This precondition is asserted.
* This function is O(1).
*/
static void UnhookFrameFromSiblings(nsIFrame* aFrame);
nsIFrame* mFirstChild;
nsIFrame* mLastChild;
};
+namespace mozilla {
+namespace layout {
+
+/**
+ * Simple "auto_ptr" for nsFrameLists allocated from the shell arena.
+ * The frame list given to the constructor will be deallocated (if non-null)
+ * in the destructor. The frame list must then be empty.
+ */
+class AutoFrameListPtr {
+public:
+ AutoFrameListPtr(nsPresContext* aPresContext, nsFrameList* aFrameList)
+ : mPresContext(aPresContext), mFrameList(aFrameList) {}
+ ~AutoFrameListPtr();
+ operator nsFrameList*() const { return mFrameList; }
+ nsFrameList* operator->() const { return mFrameList; }
+private:
+ nsPresContext* mPresContext;
+ nsFrameList* mFrameList;
+};
+
+namespace detail {
+union AlignedFrameListBytes {
+ void* ptr;
+ char bytes[sizeof(nsFrameList)];
+};
+extern const AlignedFrameListBytes gEmptyFrameListBytes;
+}
+}
+}
+
+/* static */ inline const nsFrameList&
+nsFrameList::EmptyList()
+{
+ return *reinterpret_cast<const nsFrameList*>(&mozilla::layout::detail::gEmptyFrameListBytes);
+}
+
#endif /* nsFrameList_h___ */
--- a/layout/generic/nsInlineFrame.cpp
+++ b/layout/generic/nsInlineFrame.cpp
@@ -22,16 +22,17 @@
#include "nsDisplayList.h"
#include "mozilla/Likely.h"
#ifdef DEBUG
#undef NOISY_PUSHING
#endif
using namespace mozilla;
+using namespace mozilla::layout;
//////////////////////////////////////////////////////////////////////
// Basic nsInlineFrame methods
nsIFrame*
NS_NewInlineFrame(nsIPresShell* aPresShell, nsStyleContext* aContext)
@@ -307,19 +308,19 @@ nsInlineFrame::Reflow(nsPresContext*
}
bool lazilySetParentPointer = false;
nsIFrame* lineContainer = aReflowState.mLineLayout->GetLineContainerFrame();
// Check for an overflow list with our prev-in-flow
nsInlineFrame* prevInFlow = (nsInlineFrame*)GetPrevInFlow();
- if (nullptr != prevInFlow) {
- nsAutoPtr<nsFrameList> prevOverflowFrames(prevInFlow->StealOverflowFrames());
-
+ if (prevInFlow) {
+ AutoFrameListPtr prevOverflowFrames(aPresContext,
+ prevInFlow->StealOverflowFrames());
if (prevOverflowFrames) {
// When pushing and pulling frames we need to check for whether any
// views need to be reparented.
nsContainerFrame::ReparentFrameViewList(aPresContext,
*prevOverflowFrames,
prevInFlow, this);
// Check if we should do the lazilySetParentPointer optimization.
@@ -365,17 +366,17 @@ nsInlineFrame::Reflow(nsPresContext*
// However, add an assertion in case we get reflowed more than once with
// the initial reflow reason
nsFrameList* overflowFrames = GetOverflowFrames();
NS_ASSERTION(!overflowFrames || overflowFrames->IsEmpty(),
"overflow list is not empty for initial reflow");
}
#endif
if (!(GetStateBits() & NS_FRAME_FIRST_REFLOW)) {
- nsAutoPtr<nsFrameList> overflowFrames(StealOverflowFrames());
+ AutoFrameListPtr overflowFrames(aPresContext, StealOverflowFrames());
if (overflowFrames) {
NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
if (!lazilySetParentPointer) {
// The frames on our own overflowlist may have been pushed by a
// previous lazilySetParentPointer Reflow so we need to ensure
// the correct parent pointer now since we're not setting it
// lazily in this Reflow.
nsIFrame* firstChild = overflowFrames->FirstChild();
@@ -430,20 +431,22 @@ nsInlineFrame::CanContinueTextRun() cons
return true;
}
/* virtual */ void
nsInlineFrame::PullOverflowsFromPrevInFlow()
{
nsInlineFrame* prevInFlow = static_cast<nsInlineFrame*>(GetPrevInFlow());
if (prevInFlow) {
- nsAutoPtr<nsFrameList> prevOverflowFrames(prevInFlow->StealOverflowFrames());
+ nsPresContext* presContext = PresContext();
+ AutoFrameListPtr prevOverflowFrames(presContext,
+ prevInFlow->StealOverflowFrames());
if (prevOverflowFrames) {
// Assume that our prev-in-flow has the same line container that we do.
- nsContainerFrame::ReparentFrameViewList(PresContext(),
+ nsContainerFrame::ReparentFrameViewList(presContext,
*prevOverflowFrames,
prevInFlow, this);
mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
}
}
}
nsresult
@@ -762,39 +765,38 @@ nsIFrame*
nsInlineFrame::PullOneFrame(nsPresContext* aPresContext,
InlineReflowState& irs,
bool* aIsComplete)
{
bool isComplete = true;
nsIFrame* frame = nullptr;
nsInlineFrame* nextInFlow = irs.mNextInFlow;
- while (nullptr != nextInFlow) {
+ while (nextInFlow) {
frame = nextInFlow->mFrames.FirstChild();
if (!frame) {
// The nextInFlow's principal list has no frames, try its overflow list.
nsFrameList* overflowFrames = nextInFlow->GetOverflowFrames();
if (overflowFrames) {
- frame = overflowFrames->FirstChild();
- if (!frame->GetNextSibling()) {
+ frame = overflowFrames->RemoveFirstChild();
+ if (overflowFrames->IsEmpty()) {
// We're stealing the only frame - delete the overflow list.
- delete nextInFlow->StealOverflowFrames();
+ nextInFlow->DestroyOverflowList(aPresContext);
} else {
// We leave the remaining frames on the overflow list (rather than
// putting them on nextInFlow's principal list) so we don't have to
// set up the parent for them.
- overflowFrames->RemoveFirstChild();
}
// ReparentFloatsForInlineChild needs it to be on a child list -
// we remove it again below.
nextInFlow->mFrames.SetFrames(frame);
}
}
- if (nullptr != frame) {
+ if (frame) {
// If our block has no next continuation, then any floats belonging to
// the pulled frame must belong to our block already. This check ensures
// we do no extra work in the common non-vertical-breaking case.
if (irs.mLineContainer && irs.mLineContainer->GetNextContinuation()) {
// The blockChildren.ContainsFrame check performed by
// ReparentFloatsForInlineChild will be fast because frame's ancestor
// will be the first child of its containing block.
ReparentFloatsForInlineChild(irs.mLineContainer, frame, false);
@@ -805,17 +807,17 @@ nsInlineFrame::PullOneFrame(nsPresContex
mFrames.InsertFrame(this, irs.mPrevFrame, frame);
isComplete = false;
if (irs.mLineLayout) {
irs.mLineLayout->SetDirtyNextLine();
}
nsContainerFrame::ReparentFrameView(aPresContext, frame, nextInFlow, this);
break;
}
- nextInFlow = (nsInlineFrame*) nextInFlow->GetNextInFlow();
+ nextInFlow = static_cast<nsInlineFrame*>(nextInFlow->GetNextInFlow());
irs.mNextInFlow = nextInFlow;
}
*aIsComplete = isComplete;
return frame;
}
void
@@ -973,33 +975,34 @@ nsFirstLineFrame::Reflow(nsPresContext*
if (nullptr == aReflowState.mLineLayout) {
return NS_ERROR_INVALID_ARG;
}
nsIFrame* lineContainer = aReflowState.mLineLayout->GetLineContainerFrame();
// Check for an overflow list with our prev-in-flow
nsFirstLineFrame* prevInFlow = (nsFirstLineFrame*)GetPrevInFlow();
- if (nullptr != prevInFlow) {
- nsAutoPtr<nsFrameList> prevOverflowFrames(prevInFlow->StealOverflowFrames());
+ if (prevInFlow) {
+ AutoFrameListPtr prevOverflowFrames(aPresContext,
+ prevInFlow->StealOverflowFrames());
if (prevOverflowFrames) {
// Assign all floats to our block if necessary
if (lineContainer && lineContainer->GetPrevContinuation()) {
ReparentFloatsForInlineChild(lineContainer,
prevOverflowFrames->FirstChild(),
true);
}
const nsFrameList::Slice& newFrames =
mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
ReparentChildListStyle(aPresContext, newFrames, this);
}
}
// It's also possible that we have an overflow list for ourselves
- nsAutoPtr<nsFrameList> overflowFrames(StealOverflowFrames());
+ AutoFrameListPtr overflowFrames(aPresContext, StealOverflowFrames());
if (overflowFrames) {
NS_ASSERTION(mFrames.NotEmpty(), "overflow list w/o frames");
const nsFrameList::Slice& newFrames =
mFrames.AppendFrames(nullptr, *overflowFrames);
ReparentChildListStyle(aPresContext, newFrames, this);
}
@@ -1079,18 +1082,20 @@ nsFirstLineFrame::Reflow(nsPresContext*
return rv;
}
/* virtual */ void
nsFirstLineFrame::PullOverflowsFromPrevInFlow()
{
nsFirstLineFrame* prevInFlow = static_cast<nsFirstLineFrame*>(GetPrevInFlow());
if (prevInFlow) {
- nsAutoPtr<nsFrameList> prevOverflowFrames(prevInFlow->StealOverflowFrames());
+ nsPresContext* presContext = PresContext();
+ AutoFrameListPtr prevOverflowFrames(presContext,
+ prevInFlow->StealOverflowFrames());
if (prevOverflowFrames) {
// Assume that our prev-in-flow has the same line container that we do.
const nsFrameList::Slice& newFrames =
mFrames.InsertFrames(this, nullptr, *prevOverflowFrames);
- ReparentChildListStyle(PresContext(), newFrames, this);
+ ReparentChildListStyle(presContext, newFrames, this);
}
}
}
--- a/layout/reftests/svg/text/reftest.list
+++ b/layout/reftests/svg/text/reftest.list
@@ -160,15 +160,18 @@ HTTP(../..) == clipPath-applied.svg clip
# text and patterns
# == pattern-content.svg pattern-content-ref.svg # disabled due to infinite invalidation loop bug
# text and filters
HTTP(../..) == filter-applied.svg filter-applied-ref.svg
# selection
+needs-focus == selectSubString.svg selectSubString-ref.svg
+needs-focus == selectSubString-2.svg selectSubString-2-ref.svg
+needs-focus == selectSubString-3.svg selectSubString-3-ref.svg
needs-focus == simple-selection.svg simple-selection-ref.html
needs-focus == simple-bidi-selection.svg simple-bidi-selection-ref.html
needs-focus == simple-fill-color-selection.svg simple-fill-color-selection-ref.html
needs-focus == simple-underline-selection.svg simple-underline-selection-ref.html
needs-focus == multiple-text-selection.svg multiple-text-selection-ref.html
needs-focus == multiple-chunks-selection.svg multiple-chunks-selection-ref.svg
needs-focus == textpath-selection.svg textpath-selection-ref.svg
copy from layout/reftests/svg/text/simple-selection.svg
copy to layout/reftests/svg/text/selectSubString-2-ref.svg
--- a/layout/reftests/svg/text/simple-selection.svg
+++ b/layout/reftests/svg/text/selectSubString-2-ref.svg
@@ -4,13 +4,13 @@
-->
<svg xmlns="http://www.w3.org/2000/svg" width="700" height="200">
<g transform="translate(100,100)" style="font: 16px sans-serif">
<text>hello</text>
</g>
<script>
var text = document.getElementsByTagName("text")[0];
var range = document.createRange();
- range.setStart(text.firstChild, 1);
- range.setEnd(text.firstChild, 4);
+ range.setStart(text.firstChild, 0);
+ range.setEnd(text.firstChild, 5);
window.getSelection().addRange(range);
</script>
</svg>
copy from layout/reftests/svg/text/simple-selection.svg
copy to layout/reftests/svg/text/selectSubString-2.svg
--- a/layout/reftests/svg/text/simple-selection.svg
+++ b/layout/reftests/svg/text/selectSubString-2.svg
@@ -3,14 +3,11 @@
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg" width="700" height="200">
<g transform="translate(100,100)" style="font: 16px sans-serif">
<text>hello</text>
</g>
<script>
var text = document.getElementsByTagName("text")[0];
- var range = document.createRange();
- range.setStart(text.firstChild, 1);
- range.setEnd(text.firstChild, 4);
- window.getSelection().addRange(range);
+ text.selectSubString(0, 5);
</script>
</svg>
copy from layout/reftests/svg/text/simple-selection.svg
copy to layout/reftests/svg/text/selectSubString-3-ref.svg
--- a/layout/reftests/svg/text/simple-selection.svg
+++ b/layout/reftests/svg/text/selectSubString-3-ref.svg
@@ -1,16 +1,16 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg" width="700" height="200">
<g transform="translate(100,100)" style="font: 16px sans-serif">
- <text>hello</text>
+ <text>abc אבג 123 דהו def</text>
</g>
<script>
var text = document.getElementsByTagName("text")[0];
var range = document.createRange();
- range.setStart(text.firstChild, 1);
- range.setEnd(text.firstChild, 4);
+ range.setStart(text.firstChild, 0);
+ range.setEnd(text.firstChild, 9);
window.getSelection().addRange(range);
</script>
</svg>
copy from layout/reftests/svg/text/simple-selection.svg
copy to layout/reftests/svg/text/selectSubString-3.svg
--- a/layout/reftests/svg/text/simple-selection.svg
+++ b/layout/reftests/svg/text/selectSubString-3.svg
@@ -1,16 +1,15 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg" width="700" height="200">
<g transform="translate(100,100)" style="font: 16px sans-serif">
- <text>hello</text>
+ <text>
+ abc אבג 123 דהו def
+ </text>
</g>
<script>
var text = document.getElementsByTagName("text")[0];
- var range = document.createRange();
- range.setStart(text.firstChild, 1);
- range.setEnd(text.firstChild, 4);
- window.getSelection().addRange(range);
+ text.selectSubString(0, 9);
</script>
</svg>
copy from layout/reftests/svg/text/simple-selection.svg
copy to layout/reftests/svg/text/selectSubString-ref.svg
copy from layout/reftests/svg/text/simple-selection.svg
copy to layout/reftests/svg/text/selectSubString.svg
--- a/layout/reftests/svg/text/simple-selection.svg
+++ b/layout/reftests/svg/text/selectSubString.svg
@@ -3,14 +3,11 @@
http://creativecommons.org/publicdomain/zero/1.0/
-->
<svg xmlns="http://www.w3.org/2000/svg" width="700" height="200">
<g transform="translate(100,100)" style="font: 16px sans-serif">
<text>hello</text>
</g>
<script>
var text = document.getElementsByTagName("text")[0];
- var range = document.createRange();
- range.setStart(text.firstChild, 1);
- range.setEnd(text.firstChild, 4);
- window.getSelection().addRange(range);
+ text.selectSubString(1, 3);
</script>
</svg>
--- a/layout/svg/nsSVGTextFrame2.cpp
+++ b/layout/svg/nsSVGTextFrame2.cpp
@@ -2035,20 +2035,20 @@ public:
/**
* Advances ahead aCount matching characters. Returns true if there were
* enough characters to advance past, and false otherwise.
*/
bool Next(uint32_t aCount);
/**
- * Advances ahead up to aCount matching characters, stopping early if we move
- * past the subtree (if one was specified in the constructor).
+ * Advances ahead up to aCount matching characters, returns true if there
+ * were enough characters to advance to.
*/
- void NextWithinSubtree(uint32_t aCount);
+ bool NextWithinSubtree(uint32_t aCount);
/**
* Advances to the character with the specified index. The index is in the
* space of original characters (i.e., all DOM characters under the <text>
* that are within valid text content elements).
*/
bool AdvanceToCharacter(uint32_t aTextElementCharIndex);
@@ -2300,25 +2300,26 @@ CharIterator::Next(uint32_t aCount)
if (!Next()) {
return false;
}
aCount--;
}
return true;
}
-void
+bool
CharIterator::NextWithinSubtree(uint32_t aCount)
{
while (IsWithinSubtree() && aCount) {
+ --aCount;
if (!Next()) {
break;
}
- aCount--;
- }
+ }
+ return !aCount;
}
bool
CharIterator::AdvanceToCharacter(uint32_t aTextElementCharIndex)
{
while (mTextElementCharIndex < aTextElementCharIndex) {
if (!Next()) {
return false;
@@ -3645,16 +3646,48 @@ nsSVGTextFrame2::GetComputedTextLength(n
length += run.GetAdvanceWidth();
}
return PresContext()->AppUnitsToGfxUnits(length) *
cssPxPerDevPx / mFontSizeScaleFactor;
}
/**
+ * Implements the SVG DOM SelectSubString method for the specified
+ * text content element.
+ */
+nsresult
+nsSVGTextFrame2::SelectSubString(nsIContent* aContent,
+ uint32_t charnum, uint32_t nchars)
+{
+ UpdateGlyphPositioning(false);
+
+ // Convert charnum/nchars from addressable characters relative to
+ // aContent to global character indices.
+ CharIterator chit(this, CharIterator::eAddressable, aContent);
+ if (!chit.AdvanceToSubtree() ||
+ !chit.Next(charnum) ||
+ chit.IsAfterSubtree()) {
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+ }
+ charnum = chit.TextElementCharIndex();
+ nsIContent* content = chit.TextFrame()->GetContent();
+ if (!chit.NextWithinSubtree(nchars)) {
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+ }
+ nchars = chit.TextElementCharIndex() - charnum;
+
+ nsRefPtr<nsFrameSelection> frameSelection = GetFrameSelection();
+
+ frameSelection->HandleClick(content, charnum, charnum + nchars,
+ false, false, false);
+ return NS_OK;
+}
+
+/**
* Implements the SVG DOM GetSubStringLength method for the specified
* text content element.
*/
nsresult
nsSVGTextFrame2::GetSubStringLength(nsIContent* aContent,
uint32_t charnum, uint32_t nchars,
float* aResult)
{
@@ -3670,17 +3703,19 @@ nsSVGTextFrame2::GetSubStringLength(nsIC
}
if (nchars == 0) {
*aResult = 0.0f;
return NS_OK;
}
charnum = chit.TextElementCharIndex();
- chit.NextWithinSubtree(nchars);
+ if (!chit.NextWithinSubtree(nchars)) {
+ return NS_ERROR_DOM_INDEX_SIZE_ERR;
+ }
nchars = chit.TextElementCharIndex() - charnum;
// Find each rendered run that intersects with the range defined
// by charnum/nchars.
nscoord textLength = 0;
TextRenderedRunIterator it(this, TextRenderedRunIterator::eAllFrames);
TextRenderedRun run = it.Current();
while (run.mFrame) {
--- a/layout/svg/nsSVGTextFrame2.h
+++ b/layout/svg/nsSVGTextFrame2.h
@@ -254,16 +254,17 @@ public:
uint32_t aFlags);
// nsSVGContainerFrame methods:
virtual gfxMatrix GetCanvasTM(uint32_t aFor);
// SVG DOM text methods:
uint32_t GetNumberOfChars(nsIContent* aContent);
float GetComputedTextLength(nsIContent* aContent);
+ nsresult SelectSubString(nsIContent* aContent, uint32_t charnum, uint32_t nchars);
nsresult GetSubStringLength(nsIContent* aContent, uint32_t charnum,
uint32_t nchars, float* aResult);
int32_t GetCharNumAtPosition(nsIContent* aContent, mozilla::nsISVGPoint* point);
nsresult GetStartPositionOfChar(nsIContent* aContent, uint32_t aCharNum,
mozilla::nsISVGPoint** aResult);
nsresult GetEndPositionOfChar(nsIContent* aContent, uint32_t aCharNum,
mozilla::nsISVGPoint** aResult);
--- a/layout/tables/nsTableRowGroupFrame.cpp
+++ b/layout/tables/nsTableRowGroupFrame.cpp
@@ -17,16 +17,17 @@
#include "nsHTMLParts.h"
#include "nsCSSFrameConstructor.h"
#include "nsDisplayList.h"
#include "nsCellMap.h"//table cell navigation
#include <algorithm>
using namespace mozilla;
+using namespace mozilla::layout;
nsTableRowGroupFrame::nsTableRowGroupFrame(nsStyleContext* aContext):
nsContainerFrame(aContext)
{
SetRepeatable(false);
}
nsTableRowGroupFrame::~nsTableRowGroupFrame()
@@ -966,31 +967,31 @@ nsTableRowGroupFrame::UndoContinuedRow(n
{
if (!aRow) return; // allow null aRow to avoid callers doing null checks
// rowBefore was the prev-sibling of aRow's next-sibling before aRow was created
nsTableRowFrame* rowBefore = (nsTableRowFrame*)aRow->GetPrevInFlow();
NS_PRECONDITION(mFrames.ContainsFrame(rowBefore),
"rowBefore not in our frame list?");
- nsAutoPtr<nsFrameList> overflows(StealOverflowFrames());
+ AutoFrameListPtr overflows(aPresContext, StealOverflowFrames());
if (!rowBefore || !overflows || overflows->IsEmpty() ||
overflows->FirstChild() != aRow) {
NS_ERROR("invalid continued row");
return;
}
// Destroy aRow, its cells, and their cell blocks. Cell blocks that have split
// will not have reflowed yet to pick up content from any overflow lines.
overflows->DestroyFrame(aRow);
- if (overflows->IsEmpty())
- return;
// Put the overflow rows into our child list
- mFrames.InsertFrames(nullptr, rowBefore, *overflows);
+ if (!overflows->IsEmpty()) {
+ mFrames.InsertFrames(nullptr, rowBefore, *overflows);
+ }
}
static nsTableRowFrame*
GetRowBefore(nsTableRowFrame& aStartRow,
nsTableRowFrame& aRow)
{
nsTableRowFrame* rowBefore = nullptr;
for (nsTableRowFrame* sib = &aStartRow; sib && (sib != &aRow); sib = sib->GetNextRow()) {
--- a/layout/xul/base/src/nsMenuFrame.cpp
+++ b/layout/xul/base/src/nsMenuFrame.cpp
@@ -301,28 +301,28 @@ void
nsMenuFrame::DestroyPopupList()
{
NS_ASSERTION(HasPopup(), "huh?");
nsFrameList* prop =
static_cast<nsFrameList*>(Properties().Remove(PopupListProperty()));
NS_ASSERTION(prop && prop->IsEmpty(),
"popup list must exist and be empty when destroying");
RemoveStateBits(NS_STATE_MENU_HAS_POPUP_LIST);
- delete prop;
+ prop->Delete(PresContext()->PresShell());
}
void
nsMenuFrame::SetPopupFrame(nsFrameList& aFrameList)
{
for (nsFrameList::Enumerator e(aFrameList); !e.AtEnd(); e.Next()) {
nsMenuPopupFrame* popupFrame = do_QueryFrame(e.get());
if (popupFrame) {
// Remove the frame from the list and store it in a nsFrameList* property.
aFrameList.RemoveFrame(popupFrame);
- nsFrameList* popupList = new nsFrameList(popupFrame, popupFrame);
+ nsFrameList* popupList = new (PresContext()->PresShell()) nsFrameList(popupFrame, popupFrame);
Properties().Set(PopupListProperty(), popupList);
AddStateBits(NS_STATE_MENU_HAS_POPUP_LIST);
break;
}
}
}
NS_IMETHODIMP
--- a/media/mtransport/test/sctp_unittest.cpp
+++ b/media/mtransport/test/sctp_unittest.cpp
@@ -70,16 +70,17 @@ class TransportTestPeer : public sigslot
sctp_(usrsctp_socket(AF_CONN, SOCK_STREAM, IPPROTO_SCTP, receive_cb, nullptr, 0, nullptr)),
timer_(do_CreateInstance(NS_TIMER_CONTRACTID)),
periodic_(nullptr) {
std::cerr << "Creating TransportTestPeer; flow=" <<
static_cast<void *>(flow_.get()) <<
" local=" << local_port <<
" remote=" << remote_port << std::endl;
+ usrsctp_register_address(static_cast<void *>(this));
int r = usrsctp_set_non_blocking(sctp_, 1);
EXPECT_GE(r, 0);
struct linger l;
l.l_onoff = 1;
l.l_linger = 0;
r = usrsctp_setsockopt(sctp_, SOL_SOCKET, SO_LINGER, &l,
(socklen_t)sizeof(l));
@@ -95,17 +96,17 @@ class TransportTestPeer : public sigslot
EXPECT_GE(r, 0);
memset(&local_addr_, 0, sizeof(local_addr_));
local_addr_.sconn_family = AF_CONN;
#if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && !defined(__Userspace_os_Android)
local_addr_.sconn_len = sizeof(struct sockaddr_conn);
#endif
local_addr_.sconn_port = htons(local_port);
- local_addr_.sconn_addr = nullptr;
+ local_addr_.sconn_addr = static_cast<void *>(this);
memset(&remote_addr_, 0, sizeof(remote_addr_));
remote_addr_.sconn_family = AF_CONN;
#if !defined(__Userspace_os_Linux) && !defined(__Userspace_os_Windows) && !defined(__Userspace_os_Android)
remote_addr_.sconn_len = sizeof(struct sockaddr_conn);
#endif
remote_addr_.sconn_port = htons(remote_port);
@@ -115,16 +116,17 @@ class TransportTestPeer : public sigslot
res = loopback_->Init();
EXPECT_EQ((nsresult)NS_OK, res);
}
~TransportTestPeer() {
std::cerr << "Destroying sctp connection flow=" <<
static_cast<void *>(flow_.get()) << std::endl;
usrsctp_close(sctp_);
+ usrsctp_deregister_address(static_cast<void *>(this));
test_utils->sts_target()->Dispatch(WrapRunnable(this,
&TransportTestPeer::DisconnectInt),
NS_DISPATCH_SYNC);
std::cerr << "~TransportTestPeer() completed" << std::endl;
}
@@ -342,17 +344,17 @@ class TransportTest : public ::testing::
TransportTestPeer *p1_;
TransportTestPeer *p2_;
};
TEST_F(TransportTest, TestConnect) {
ConnectSocket();
}
-TEST_F(TransportTest, DISABLED_TestConnectSymmetricalPorts) {
+TEST_F(TransportTest, TestConnectSymmetricalPorts) {
ConnectSocket(5002,5002);
}
TEST_F(TransportTest, TestTransfer) {
ConnectSocket();
TestTransfer(50);
}
--- a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
+++ b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
@@ -46,19 +46,21 @@ extern void lsm_start_continuous_tone_ti
extern void lsm_update_active_tone(vcm_tones_t tone, cc_call_handle_t call_handle);
extern void lsm_stop_multipart_tone_timer(void);
extern void lsm_stop_continuous_tone_timer(void);
}//end extern "C"
static const char* logTag = "VcmSipccBinding";
+// Cloned from ccapi.h
typedef enum {
CC_AUDIO_1,
- CC_VIDEO_1
+ CC_VIDEO_1,
+ CC_DATACHANNEL_1
} cc_media_cap_name;
#define SIPSDP_ILBC_MODE20 20
/* static */
using namespace CSF;
@@ -1055,27 +1057,29 @@ short vcmGetDtlsIdentity(const char *pee
* @param[in] peerconnection - the peerconnection in use
* @param[in] streams - the number of streams for this data channel
* @param[in] local_datachannel_port - the negotiated sctp port
* @param[in] remote_datachannel_port - the negotiated sctp port
* @param[in] protocol - the protocol as a string
*
* @return 0 success, error failure
*/
-static short vcmInitializeDataChannel_m(const char *peerconnection, cc_uint16_t streams,
+static short vcmInitializeDataChannel_m(const char *peerconnection,
+ int track_id, cc_uint16_t streams,
int local_datachannel_port, int remote_datachannel_port, const char* protocol)
{
nsresult res;
CSFLogDebug( logTag, "%s: PC = %s", __FUNCTION__, peerconnection);
sipcc::PeerConnectionWrapper pc(peerconnection);
ENSURE_PC(pc, VCM_ERROR);
- res = pc.impl()->InitializeDataChannel(local_datachannel_port, remote_datachannel_port, streams);
+ res = pc.impl()->InitializeDataChannel(track_id, local_datachannel_port,
+ remote_datachannel_port, streams);
if (NS_FAILED(res)) {
return VCM_ERROR;
}
return 0;
}
/* Set negotiated DataChannel parameters.
@@ -1083,24 +1087,26 @@ static short vcmInitializeDataChannel_m(
* @param[in] peerconnection - the peerconnection in use
* @param[in] streams - the number of streams for this data channel
* @param[in] local_datachannel_port - the negotiated sctp port
* @param[in] remote_datachannel_port - the negotiated sctp port
* @param[in] protocol - the protocol as a string
*
* @return 0 success, error failure
*/
-short vcmInitializeDataChannel(const char *peerconnection, cc_uint16_t streams,
+short vcmInitializeDataChannel(const char *peerconnection, int track_id,
+ cc_uint16_t streams,
int local_datachannel_port, int remote_datachannel_port, const char* protocol)
{
short ret;
mozilla::SyncRunnable::DispatchToThread(VcmSIPCCBinding::getMainThread(),
WrapRunnableNMRet(&vcmInitializeDataChannel_m,
peerconnection,
+ track_id,
streams,
local_datachannel_port,
remote_datachannel_port,
protocol,
&ret));
return ret;
}
@@ -1309,43 +1315,51 @@ static int vcmRxStartICE_m(cc_mcapid_t m
vcm_mediaAttrs_t *attrs)
{
CSFLogDebug( logTag, "%s(%s)", __FUNCTION__, peerconnection);
// Find the PC.
sipcc::PeerConnectionWrapper pc(peerconnection);
ENSURE_PC(pc, VCM_ERROR);
- if(!payloads) {
- CSFLogError( logTag, "Unitialized payload list");
- return VCM_ERROR;
+ // Datachannel will use this though not for RTP
+ mozilla::RefPtr<TransportFlow> rtp_flow =
+ vcmCreateTransportFlow(pc.impl(), level, false,
+ fingerprint_alg, fingerprint);
+ if (!rtp_flow) {
+ CSFLogError( logTag, "Could not create RTP flow");
+ return VCM_ERROR;
+ }
+
+ if (CC_IS_DATACHANNEL(mcap_id)) {
+ // That's all we need for DataChannels - a flow registered
+ CSFLogDebug( logTag, "%s success", __FUNCTION__);
+ return 0;
+ }
+
+ if (!payloads) {
+ CSFLogError( logTag, "Unitialized payload list");
+ return VCM_ERROR;
}
// Find the stream we need
nsRefPtr<sipcc::RemoteSourceStreamInfo> stream =
pc.impl()->media()->GetRemoteStream(pc_stream_id);
if (!stream) {
// This should never happen
PR_ASSERT(PR_FALSE);
return VCM_ERROR;
}
- // Create the transport flows
- mozilla::RefPtr<TransportFlow> rtp_flow =
- vcmCreateTransportFlow(pc.impl(), level, false,
- fingerprint_alg, fingerprint);
- if (!rtp_flow) {
- CSFLogError( logTag, "Could not create RTP flow");
- return VCM_ERROR;
- }
+
mozilla::RefPtr<TransportFlow> rtcp_flow =
- vcmCreateTransportFlow(pc.impl(), level, true,
- fingerprint_alg, fingerprint);
+ vcmCreateTransportFlow(pc.impl(), level, true,
+ fingerprint_alg, fingerprint);
if (!rtcp_flow) {
- CSFLogError( logTag, "Could not create RTCP flow");
- return VCM_ERROR;
+ CSFLogError( logTag, "Could not create RTCP flow");
+ return VCM_ERROR;
}
if (CC_IS_AUDIO(mcap_id)) {
std::vector<mozilla::AudioCodecConfig *> configs;
// Instantiate an appropriate conduit
mozilla::RefPtr<mozilla::AudioSessionConduit> tx_conduit =
pc.impl()->media()->GetConduit(level, false);
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.cpp
@@ -265,17 +265,18 @@ PeerConnectionImpl::PeerConnectionImpl()
, mReadyState(kNew)
, mIceState(kIceGathering)
, mPCObserver(NULL)
, mWindow(NULL)
, mIdentity(NULL)
, mSTSThread(NULL)
, mMedia(new PeerConnectionMedia(this))
, mNumAudioStreams(0)
- , mNumVideoStreams(0) {
+ , mNumVideoStreams(0)
+ , mHaveDataStream(false) {
#ifdef MOZILLA_INTERNAL_API
MOZ_ASSERT(NS_IsMainThread());
#endif
}
PeerConnectionImpl::~PeerConnectionImpl()
{
// This aborts if not on main thread (in Debug builds)
@@ -622,96 +623,113 @@ PeerConnectionImpl::CreateFakeMediaStrea
}
}
return NS_OK;
}
// Stubbing this call out for now.
// We can remove it when we are confident of datachannels being started
-// correctly on SDP negotiation
+// correctly on SDP negotiation (bug 852908)
NS_IMETHODIMP
PeerConnectionImpl::ConnectDataConnection(uint16_t aLocalport,
uint16_t aRemoteport,
uint16_t aNumstreams)
{
return NS_OK; // InitializeDataChannel(aLocalport, aRemoteport, aNumstreams);
}
// Data channels won't work without a window, so in order for the C++ unit
// tests to work (it doesn't have a window available) we ifdef the following
// two implementations.
-nsresult
-PeerConnectionImpl::InitializeDataChannel(uint16_t aLocalport,
- uint16_t aRemoteport,
- uint16_t aNumstreams)
+NS_IMETHODIMP
+PeerConnectionImpl::EnsureDataConnection(uint16_t aNumstreams)
{
PC_AUTO_ENTER_API_CALL_NO_CHECK();
#ifdef MOZILLA_INTERNAL_API
if (mDataConnection) {
- CSFLogError(logTag,"%s DataConnection already connected",__FUNCTION__);
+ CSFLogDebug(logTag,"%s DataConnection already connected",__FUNCTION__);
// Ignore the request to connect when already connected. This entire
// implementation is temporary. Ignore aNumstreams as it's merely advisory
// and we increase the number of streams dynamically as needed.
return NS_OK;
}
mDataConnection = new mozilla::DataChannelConnection(this);
- if (!mDataConnection->Init(aLocalport, aNumstreams, true)) {
+ if (!mDataConnection->Init(5000, aNumstreams, true)) {
CSFLogError(logTag,"%s DataConnection Init Failed",__FUNCTION__);
return NS_ERROR_FAILURE;
}
- // XXX Fix! Get the correct flow for DataChannel. Also error handling.
- for (int i = 2; i >= 0; i--) {
- nsRefPtr<TransportFlow> flow = mMedia->GetTransportFlow(i,false).get();
- CSFLogDebug(logTag, "Transportflow[%d] = %p", i, flow.get());
+#endif
+ return NS_OK;
+}
+
+nsresult
+PeerConnectionImpl::InitializeDataChannel(int track_id,
+ uint16_t aLocalport,
+ uint16_t aRemoteport,
+ uint16_t aNumstreams)
+{
+ PC_AUTO_ENTER_API_CALL_NO_CHECK();
+
+#ifdef MOZILLA_INTERNAL_API
+ nsresult rv = EnsureDataConnection(aNumstreams);
+ if (NS_SUCCEEDED(rv)) {
+ // use the specified TransportFlow
+ nsRefPtr<TransportFlow> flow = mMedia->GetTransportFlow(track_id, false).get();
+ CSFLogDebug(logTag, "Transportflow[%d] = %p", track_id, flow.get());
if (flow) {
- if (!mDataConnection->ConnectDTLS(flow, aLocalport, aRemoteport)) {
- return NS_ERROR_FAILURE;
+ if (mDataConnection->ConnectViaTransportFlow(flow, aLocalport, aRemoteport)) {
+ return NS_OK;
}
- break;
}
}
- return NS_OK;
-#else
- return NS_ERROR_FAILURE;
+ mDataConnection = nullptr;
#endif
+ return NS_ERROR_FAILURE;
}
NS_IMETHODIMP
PeerConnectionImpl::CreateDataChannel(const nsACString& aLabel,
+ const nsACString& aProtocol,
uint16_t aType,
bool outOfOrderAllowed,
uint16_t aMaxTime,
uint16_t aMaxNum,
+ bool aExternalNegotiated,
+ uint16_t aStream,
nsIDOMDataChannel** aRetval)
{
PC_AUTO_ENTER_API_CALL_NO_CHECK();
MOZ_ASSERT(aRetval);
#ifdef MOZILLA_INTERNAL_API
nsRefPtr<mozilla::DataChannel> dataChannel;
mozilla::DataChannelConnection::Type theType =
static_cast<mozilla::DataChannelConnection::Type>(aType);
- if (!mDataConnection) {
- return NS_ERROR_FAILURE;
+ nsresult rv = EnsureDataConnection(WEBRTC_DATACHANNEL_STREAMS_DEFAULT);
+ if (NS_FAILED(rv)) {
+ return rv;
}
dataChannel = mDataConnection->Open(
- aLabel, theType, !outOfOrderAllowed,
+ aLabel, aProtocol, theType, !outOfOrderAllowed,
aType == mozilla::DataChannelConnection::PARTIAL_RELIABLE_REXMIT ? aMaxNum :
(aType == mozilla::DataChannelConnection::PARTIAL_RELIABLE_TIMED ? aMaxTime : 0),
- nullptr, nullptr
+ nullptr, nullptr, aExternalNegotiated, aStream
);
NS_ENSURE_TRUE(dataChannel,NS_ERROR_FAILURE);
CSFLogDebug(logTag, "%s: making DOMDataChannel", __FUNCTION__);
- // TODO -- need something like "mCall->addStream(stream_id, 0, DATA);" so
- // the SDP can be generated correctly
+ if (!mHaveDataStream) {
+ // XXX stream_id of 0 might confuse things...
+ mCall->addStream(0, 2, DATA);
+ mHaveDataStream = true;
+ }
return NS_NewDOMDataChannel(dataChannel.forget(), mWindow, aRetval);
#else
return NS_OK;
#endif
}
void
--- a/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
+++ b/media/webrtc/signaling/src/peerconnection/PeerConnectionImpl.h
@@ -231,18 +231,18 @@ public:
return Initialize(aObserver, aWindow, &aConfiguration, nullptr, aThread, nullptr);
}
// Validate constraints and construct a MediaConstraints object
// from a JS::Value.
NS_IMETHODIMP CreateOffer(MediaConstraints& aConstraints);
NS_IMETHODIMP CreateAnswer(MediaConstraints& aConstraints);
- nsresult
- InitializeDataChannel(uint16_t aLocalport, uint16_t aRemoteport, uint16_t aNumstreams);
+ nsresult InitializeDataChannel(int track_id, uint16_t aLocalport,
+ uint16_t aRemoteport, uint16_t aNumstreams);
// Called whenever something is unrecognized by the parser
// May be called more than once and does not necessarily mean
// that parsing was stopped, only that something was unrecognized.
void OnSdpParseError(const char* errorMessage);
// Called when OnLocal/RemoteDescriptionSuccess/Error
// is called to start the list over.
@@ -254,16 +254,17 @@ private:
nsresult Initialize(IPeerConnectionObserver* aObserver,
nsIDOMWindow* aWindow,
const IceConfiguration* aConfiguration,
const JS::Value* aRTCConfiguration,
nsIThread* aThread,
JSContext* aCx);
NS_IMETHODIMP CreateOfferInt(MediaConstraints& constraints);
NS_IMETHODIMP CreateAnswerInt(MediaConstraints& constraints);
+ NS_IMETHODIMP EnsureDataConnection(uint16_t aNumstreams);
nsresult CloseInt(bool aIsSynchronous);
void ChangeReadyState(ReadyState aReadyState);
nsresult CheckApiState(bool assert_ice_ready) const;
void CheckThread() const {
NS_ABORT_IF_FALSE(CheckThreadInt(), "Wrong thread");
}
bool CheckThreadInt() const {
@@ -333,16 +334,18 @@ private:
// Temporary: used to prevent multiple audio streams or multiple video streams
// in a single PC. This is tied up in the IETF discussion around proper
// representation of multiple streams in SDP, and strongly related to
// Bug 840728.
int mNumAudioStreams;
int mNumVideoStreams;
+ bool mHaveDataStream;
+
// Holder for error messages from parsing SDP
std::vector<std::string> mSDPParseErrorMessages;
public:
//these are temporary until the DataChannel Listen/Connect API is removed
unsigned short listenPort;
unsigned short connectPort;
char *connectStr; // XXX ownership/free
--- a/media/webrtc/signaling/src/sipcc/core/gsm/fsmdef.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/fsmdef.c
@@ -3521,16 +3521,21 @@ fsmdef_ev_addstream(sm_event_t *event) {
dcb->media_cap_tbl->cap[CC_VIDEO_1].support_direction = SDP_DIRECTION_SENDRECV;
dcb->media_cap_tbl->cap[CC_VIDEO_1].pc_stream = msg->data.track.stream_id;
dcb->media_cap_tbl->cap[CC_VIDEO_1].pc_track = msg->data.track.track_id;
} else if (msg->data.track.media_type == AUDIO) {
dcb->media_cap_tbl->cap[CC_AUDIO_1].enabled = TRUE;
dcb->media_cap_tbl->cap[CC_AUDIO_1].support_direction = SDP_DIRECTION_SENDRECV;
dcb->media_cap_tbl->cap[CC_AUDIO_1].pc_stream = msg->data.track.stream_id;
dcb->media_cap_tbl->cap[CC_AUDIO_1].pc_track = msg->data.track.track_id;
+ } else if (msg->data.track.media_type == DATA) {
+ dcb->media_cap_tbl->cap[CC_DATACHANNEL_1].enabled = TRUE;
+ dcb->media_cap_tbl->cap[CC_DATACHANNEL_1].support_direction = SDP_DIRECTION_SENDRECV;
+ dcb->media_cap_tbl->cap[CC_DATACHANNEL_1].pc_stream = msg->data.track.stream_id;
+ dcb->media_cap_tbl->cap[CC_DATACHANNEL_1].pc_track = msg->data.track.track_id;
} else {
return (SM_RC_END);
}
return (SM_RC_END);
}
static sm_rcs_t
@@ -3559,21 +3564,21 @@ fsmdef_ev_removestream(sm_event_t *event
}
/*
* This is temporary code to allow configuration of the two
* default streams. When multiple streams > 2 are supported this
* will be re-implemented.
*/
if (msg->data.track.media_type == AUDIO) {
- dcb->media_cap_tbl->cap[CC_AUDIO_1].enabled = TRUE;
+ PR_ASSERT(dcb->media_cap_tbl->cap[CC_AUDIO_1].enabled);
dcb->media_cap_tbl->cap[CC_AUDIO_1].support_direction = SDP_DIRECTION_RECVONLY;
dcb->video_pref = SDP_DIRECTION_SENDRECV;
} else if (msg->data.track.media_type == VIDEO) {
- dcb->media_cap_tbl->cap[CC_VIDEO_1].enabled = TRUE;
+ PR_ASSERT(dcb->media_cap_tbl->cap[CC_VIDEO_1].enabled);
dcb->media_cap_tbl->cap[CC_VIDEO_1].support_direction = SDP_DIRECTION_RECVONLY;
} else {
return (SM_RC_END);
}
return (SM_RC_END);
}
--- a/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
@@ -142,26 +142,17 @@ static const cc_media_cap_table_t *gsmsd
dcb_p->media_cap_tbl->cap[CC_DATACHANNEL_1].name = CC_DATACHANNEL_1;
dcb_p->media_cap_tbl->cap[CC_AUDIO_1].type = SDP_MEDIA_AUDIO;
dcb_p->media_cap_tbl->cap[CC_VIDEO_1].type = SDP_MEDIA_VIDEO;
dcb_p->media_cap_tbl->cap[CC_DATACHANNEL_1].type = SDP_MEDIA_APPLICATION;
dcb_p->media_cap_tbl->cap[CC_AUDIO_1].enabled = FALSE;
dcb_p->media_cap_tbl->cap[CC_VIDEO_1].enabled = FALSE;
- /*
- * This really should be set to FALSE unless we have added
- * a data channel using createDataChannel(). Right now,
- * though, those operations are not queued (and, in fact,
- * the W3C hasn't specified the proper behavior here anyway, so
- * we would only be implementing speculatively) -- so we'll
- * always offer data channels until the standard is
- * a bit more set.
- */
- dcb_p->media_cap_tbl->cap[CC_DATACHANNEL_1].enabled = TRUE;
+ dcb_p->media_cap_tbl->cap[CC_DATACHANNEL_1].enabled = FALSE;
dcb_p->media_cap_tbl->cap[CC_AUDIO_1].support_security = TRUE;
dcb_p->media_cap_tbl->cap[CC_VIDEO_1].support_security = TRUE;
dcb_p->media_cap_tbl->cap[CC_DATACHANNEL_1].support_security = TRUE;
/* We initialize as RECVONLY to allow the application to
display incoming media streams, even if it doesn't
plan to send media for those streams. This will be
--- a/media/webrtc/signaling/src/sipcc/core/gsm/h/lsm.h
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/h/lsm.h
@@ -169,12 +169,12 @@ int lsm_get_video_mute (callid_t call_id
void lsm_set_video_window (callid_t call_id, int flags, int x, int y, int h, int w);
void lsm_get_video_window (callid_t call_id, int *flags, int *x, int *y, int *h, int *w);
boolean lsm_is_kpml_subscribed (callid_t call_id);
void
lsm_util_tone_start_with_speaker_as_backup (vcm_tones_t tone, short alert_info,
cc_call_handle_t call_handle, groupid_t group_id,
streamid_t stream_id, uint16_t direction);
-void lsm_initialize_datachannel (fsmdef_dcb_t *dcb, fsmdef_media_t *media);
+void lsm_initialize_datachannel (fsmdef_dcb_t *dcb, fsmdef_media_t *media, int track_id);
#endif //_LSM_H_
--- a/media/webrtc/signaling/src/sipcc/core/gsm/lsm.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/lsm.c
@@ -909,16 +909,24 @@ lsm_rx_start (lsm_lcb_t *lcb, const char
*/
if (media->type != SDP_MEDIA_APPLICATION &&
!gsmsdp_is_crypto_ready(media, TRUE)) {
LSM_DEBUG(DEB_L_C_F_PREFIX"%s: Not ready to open receive port (%d)\n",
DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname1), fname, media->src_port);
continue;
}
+ /* TODO(ekr@rtfm.com): Needs changing for when we
+ have > 2 streams. (adam@nostrum.com): For now,
+ we use all the same stream so pc_stream_id == 0
+ and the tracks are assigned in order and are
+ equal to the level in the media objects */
+ pc_stream_id = 0;
+ pc_track_id = media->level;
+
/*
* Open the RTP receive channel if it is not already open.
*/
LSM_DEBUG(get_debug_string(LSM_DBG_INT1), dcb->call_id, dcb->line,
fname1, "rcv chan", media->rcv_chan);
if (media->rcv_chan == FALSE) {
memset(&open_rcv, 0, sizeof(open_rcv));
@@ -973,33 +981,24 @@ lsm_rx_start (lsm_lcb_t *lcb, const char
media->rcv_chan = TRUE; /* recevied channel is created */
/* save the source RX port */
if (media->is_multicast) {
media->multicast_port = open_rcv.port;
} else {
media->src_port = open_rcv.port;
}
- /* TODO(ekr@rtfm.com): Needs changing for when we
- have > 2 streams. (adam@nostrum.com): For now,
- we use all the same stream so pc_stream_id == 0
- and the tracks are assigned in order and are
- equal to the level in the media objects */
if ( media->cap_index == CC_VIDEO_1 ) {
attrs.video.opaque = media->video;
- pc_stream_id = 0;
- pc_track_id = media->level;
} else {
attrs.audio.packetization_period = media->packetization_period;
attrs.audio.max_packetization_period = media->max_packetization_period;
attrs.audio.avt_payload_type = media->avt_payload_type;
attrs.audio.mixing_mode = mix_mode;
attrs.audio.mixing_party = mix_party;
- pc_stream_id = 0;
- pc_track_id = media->level;
}
dcb->cur_video_avail &= ~CC_ATTRIB_CAST;
config_get_value(CFGID_SDPMODE, &sdpmode, sizeof(sdpmode));
if (dcb->peerconnection) {
ret_val = vcmRxStartICE(media->cap_index, group_id, media->refid,
media->level,
pc_stream_id,
@@ -1051,20 +1050,20 @@ lsm_rx_start (lsm_lcb_t *lcb, const char
dcb->group_id,
((media != NULL) ? media->refid : CC_NO_MEDIA_REF_ID),
direction);
}
}
}
if (media->type == SDP_MEDIA_APPLICATION) {
- /* Enable datachannels
- Datachannels are always two-way so initializing only here in rx_start.
- */
- lsm_initialize_datachannel(dcb, media);
+ /* Enable datachannels
+ Datachannels are always two-way so initializing only here in rx_start.
+ */
+ lsm_initialize_datachannel(dcb, media, pc_track_id);
}
}
}
/**
* The function starts transmit channel for a given media entry.
*
* @param[in] lcb - pointer to the lsm_lcb_t.
@@ -5315,34 +5314,38 @@ void lsm_add_remote_stream (line_t line,
*
* Description:
* The function initializes the datachannel with port and
* protocol info.
*
* Parameters:
* [in] dcb - pointer to get the peerconnection id
* [in] media - pointer to get the datachannel info
+ * [in] track_id - track ID (aka m-line number)
* Returns: None
*/
-void lsm_initialize_datachannel (fsmdef_dcb_t *dcb, fsmdef_media_t *media)
+void lsm_initialize_datachannel (fsmdef_dcb_t *dcb, fsmdef_media_t *media,
+ int track_id)
{
if (!dcb) {
CSFLogError(logTag, "%s DCB is NULL", __FUNCTION__);
return;
}
if (!media) {
CSFLogError(logTag, "%s media is NULL", __FUNCTION__);
return;
}
/*
- * have access to media->streams, media->protocol, media->local/remote_datachannel_port
+ * have access to media->cap_index, media->streams, media->protocol,
+ * media->local/remote_datachannel_port
*/
- vcmInitializeDataChannel(dcb->peerconnection, media->datachannel_streams,
+ vcmInitializeDataChannel(dcb->peerconnection,
+ track_id, media->datachannel_streams,
media->local_datachannel_port, media->remote_datachannel_port,
media->datachannel_protocol);
}
/**
*
* Peform non call related action
*
--- a/media/webrtc/signaling/src/sipcc/include/cc_constants.h
+++ b/media/webrtc/signaling/src/sipcc/include/cc_constants.h
@@ -543,16 +543,17 @@ typedef unsigned int cc_media_stream_id_
typedef unsigned int cc_media_track_id_t;
typedef enum {
NO_STREAM = -1,
AUDIO,
VIDEO,
+ DATA,
TYPE_MAX
} cc_media_type_t;
typedef struct {
char *name;
char *value;
cc_boolean mandatory;
--- a/media/webrtc/signaling/src/sipcc/include/vcm.h
+++ b/media/webrtc/signaling/src/sipcc/include/vcm.h
@@ -24,16 +24,18 @@
#include "cc_constants.h"
#include "ccsdp.h"
/** Evaluates to TRUE for audio media streams where id is the mcap_id of the given stream */
#define CC_IS_AUDIO(id) ((id == CC_AUDIO_1) ? TRUE:FALSE)
/** Evaluates to TRUE for video media streams where id is the mcap_id of the given stream */
#define CC_IS_VIDEO(id) ((id == CC_VIDEO_1) ? TRUE:FALSE)
+/** Evaluates to TRUE for datachannel streams where id is the mcap_id of the given stream */
+#define CC_IS_DATACHANNEL(id) ((id == CC_DATACHANNEL_1) ? TRUE:FALSE)
/** Definitions for direction requesting Play tone to user */
#define VCM_PLAY_TONE_TO_EAR 1
/** Definitions value for direction requesting Play tone to network stream or far end */
#define VCM_PLAY_TONE_TO_NET 2
/** Definitions value for direction requesting Play tone to both user and network */
#define VCM_PLAY_TONE_TO_ALL 3
@@ -668,16 +670,17 @@ int vcmTxStart(cc_mcapid_t mcap_id,
short vcmGetDtlsIdentity(const char *peerconnection,
char *digest_alg,
size_t max_digest_alg_len,
char *digest,
size_t max_digest_len);
short vcmInitializeDataChannel(const char *peerconnection,
+ int track_id,
cc_uint16_t streams,
int local_datachannel_port,
int remote_datachannel_port,
const char* protocol);
/*!
* Close the receive stream.
*
--- a/mobile/android/base/gfx/GeckoLayerClient.java
+++ b/mobile/android/base/gfx/GeckoLayerClient.java
@@ -378,17 +378,19 @@ public class GeckoLayerClient implements
// adjust the page dimensions to account for differences in zoom
// between the rendered content (which is what Gecko tells us)
// and our zoom level (which may have diverged).
float scaleFactor = oldMetrics.zoomFactor / messageMetrics.zoomFactor;
newMetrics = oldMetrics.setPageRect(RectUtils.scale(messageMetrics.getPageRect(), scaleFactor), messageMetrics.getCssPageRect());
break;
}
- final ImmutableViewportMetrics geckoMetrics = newMetrics;
+ // Update the Gecko-side viewport metrics. Make sure to do this
+ // before modifying the metrics below.
+ final ImmutableViewportMetrics geckoMetrics = newMetrics.clamp();
post(new Runnable() {
@Override
public void run() {
mGeckoViewport = geckoMetrics;
}
});
// If we're meant to be scrolled to the top, take into account
@@ -401,17 +403,17 @@ public class GeckoLayerClient implements
if (type == ViewportMessageType.UPDATE
&& FloatUtils.fuzzyEquals(newMetrics.viewportRectTop,
newMetrics.pageRectTop)
&& oldMetrics.fixedLayerMarginTop > 0) {
newMetrics = newMetrics.setViewportOrigin(newMetrics.viewportRectLeft,
newMetrics.pageRectTop - oldMetrics.fixedLayerMarginTop);
}
- setViewportMetrics(newMetrics, type == ViewportMessageType.UPDATE, true);
+ setViewportMetrics(newMetrics, type == ViewportMessageType.UPDATE);
mDisplayPort = DisplayPortCalculator.calculate(getViewportMetrics(), null);
}
return mDisplayPort;
}
public DisplayPortMetrics getDisplayPort(boolean pageSizeUpdate, boolean isBrowserContentDisplayed, int tabId, ImmutableViewportMetrics metrics) {
Tabs tabs = Tabs.getInstance();
if (tabs.isSelectedTab(tabs.getTab(tabId)) && isBrowserContentDisplayed) {
@@ -427,17 +429,17 @@ public class GeckoLayerClient implements
return DisplayPortCalculator.calculate(metrics, null);
}
}
/**
* Sets margins on fixed-position layers, to be used when compositing.
* Must be called on the UI thread!
*/
- public void setFixedLayerMargins(float left, float top, float right, float bottom) {
+ public synchronized void setFixedLayerMargins(float left, float top, float right, float bottom) {
ImmutableViewportMetrics oldMetrics = getViewportMetrics();
ImmutableViewportMetrics newMetrics = oldMetrics.setFixedLayerMargins(left, top, right, bottom);
if (mClampOnMarginChange) {
// Only clamp on decreased margins
boolean changed = false;
float viewportRectLeft = oldMetrics.viewportRectLeft;
float viewportRectTop = oldMetrics.viewportRectTop;
@@ -466,17 +468,19 @@ public class GeckoLayerClient implements
}
// Set the new metrics, if they're different.
if (changed) {
newMetrics = newMetrics.setViewportOrigin(viewportRectLeft, viewportRectTop);
}
}
- setViewportMetrics(newMetrics, false, false);
+ mViewportMetrics = newMetrics;
+ mView.requestRender();
+ setShadowVisibility();
}
public void setClampOnFixedLayerMarginsChange(boolean aClamp) {
mClampOnMarginChange = aClamp;
}
// This is called on the Gecko thread to determine if we're still interested
// in the update of this display-port to continue. We can return true here
@@ -586,38 +590,41 @@ public class GeckoLayerClient implements
* this function is invoked on; and this function will always be called prior to syncViewportInfo.
*/
public void setFirstPaintViewport(float offsetX, float offsetY, float zoom,
float pageLeft, float pageTop, float pageRight, float pageBottom,
float cssPageLeft, float cssPageTop, float cssPageRight, float cssPageBottom) {
synchronized (this) {
ImmutableViewportMetrics currentMetrics = getViewportMetrics();
- // If we're meant to be scrolled to the top, take into account any
- // margin set on the pan zoom controller.
- if (FloatUtils.fuzzyEquals(offsetY, pageTop)) {
- offsetY = -currentMetrics.fixedLayerMarginTop;
- }
-
final ImmutableViewportMetrics newMetrics = currentMetrics
.setViewportOrigin(offsetX, offsetY)
.setZoomFactor(zoom)
.setPageRect(new RectF(pageLeft, pageTop, pageRight, pageBottom),
new RectF(cssPageLeft, cssPageTop, cssPageRight, cssPageBottom));
// Since we have switched to displaying a different document, we need to update any
// viewport-related state we have lying around. This includes mGeckoViewport and
// mViewportMetrics. Usually this information is updated via handleViewportMessage
// while we remain on the same document.
post(new Runnable() {
@Override
public void run() {
mGeckoViewport = newMetrics;
}
});
- setViewportMetrics(newMetrics);
+
+ // If we're meant to be scrolled to the top, take into account any
+ // margin set on the pan zoom controller.
+ if (FloatUtils.fuzzyEquals(offsetY, pageTop)
+ && newMetrics.fixedLayerMarginTop > 0) {
+ setViewportMetrics(newMetrics.setViewportOrigin(offsetX,
+ -newMetrics.fixedLayerMarginTop));
+ } else {
+ setViewportMetrics(newMetrics);
+ }
Tab tab = Tabs.getInstance().getSelectedTab();
mView.setBackgroundColor(tab.getBackgroundColor());
setZoomConstraints(tab.getZoomConstraints());
// At this point, we have just switched to displaying a different document than we
// we previously displaying. This means we need to abort any panning/zooming animations
// that are in progress and send an updated display port request to browser.js as soon
@@ -795,25 +802,32 @@ public class GeckoLayerClient implements
}
}
/** Implementation of PanZoomTarget
* You must hold the monitor while calling this.
*/
@Override
public void setViewportMetrics(ImmutableViewportMetrics metrics) {
- setViewportMetrics(metrics, true, true);
+ setViewportMetrics(metrics, true);
}
- private void setViewportMetrics(ImmutableViewportMetrics metrics, boolean notifyGecko, boolean keepFixedMargins) {
- if (keepFixedMargins) {
- mViewportMetrics = metrics.setFixedLayerMarginsFrom(mViewportMetrics);
- } else {
- mViewportMetrics = metrics;
- }
+ /*
+ * You must hold the monitor while calling this.
+ */
+ private void setViewportMetrics(ImmutableViewportMetrics metrics, boolean notifyGecko) {
+ // This class owns the viewport size and the fixed layer margins; don't let other pieces
+ // of code clobber either of them. The only place the viewport size should ever be
+ // updated is in GeckoLayerClient.setViewportSize, and the only place the margins should
+ // ever be updated is in GeckoLayerClient.setFixedLayerMargins; both of these assign to
+ // mViewportMetrics directly.
+ metrics = metrics.setViewportSize(mViewportMetrics.getWidth(), mViewportMetrics.getHeight());
+ metrics = metrics.setFixedLayerMarginsFrom(mViewportMetrics);
+ mViewportMetrics = metrics;
+
mView.requestRender();
if (notifyGecko && mGeckoIsReady) {
geometryChanged();
}
setShadowVisibility();
}
private void setShadowVisibility() {
--- a/mobile/android/base/gfx/ImmutableViewportMetrics.java
+++ b/mobile/android/base/gfx/ImmutableViewportMetrics.java
@@ -160,16 +160,20 @@ public class ImmutableViewportMetrics {
FloatUtils.interpolate(fixedLayerMarginLeft, to.fixedLayerMarginLeft, t),
FloatUtils.interpolate(fixedLayerMarginTop, to.fixedLayerMarginTop, t),
FloatUtils.interpolate(fixedLayerMarginRight, to.fixedLayerMarginRight, t),
FloatUtils.interpolate(fixedLayerMarginBottom, to.fixedLayerMarginBottom, t),
FloatUtils.interpolate(zoomFactor, to.zoomFactor, t));
}
public ImmutableViewportMetrics setViewportSize(float width, float height) {
+ if (FloatUtils.fuzzyEquals(width, getWidth()) && FloatUtils.fuzzyEquals(height, getHeight())) {
+ return this;
+ }
+
return new ImmutableViewportMetrics(
pageRectLeft, pageRectTop, pageRectRight, pageRectBottom,
cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom,
viewportRectLeft, viewportRectTop, viewportRectLeft + width, viewportRectTop + height,
fixedLayerMarginLeft, fixedLayerMarginTop, fixedLayerMarginRight, fixedLayerMarginBottom,
zoomFactor);
}
@@ -200,16 +204,23 @@ public class ImmutableViewportMetrics {
pageRect.left, pageRect.top, pageRect.right, pageRect.bottom,
cssPageRect.left, cssPageRect.top, cssPageRect.right, cssPageRect.bottom,
viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom,
fixedLayerMarginLeft, fixedLayerMarginTop, fixedLayerMarginRight, fixedLayerMarginBottom,
zoomFactor);
}
public ImmutableViewportMetrics setFixedLayerMargins(float left, float top, float right, float bottom) {
+ if (FloatUtils.fuzzyEquals(left, fixedLayerMarginLeft)
+ && FloatUtils.fuzzyEquals(top, fixedLayerMarginTop)
+ && FloatUtils.fuzzyEquals(right, fixedLayerMarginRight)
+ && FloatUtils.fuzzyEquals(bottom, fixedLayerMarginBottom)) {
+ return this;
+ }
+
return new ImmutableViewportMetrics(
pageRectLeft, pageRectTop, pageRectRight, pageRectBottom,
cssPageRectLeft, cssPageRectTop, cssPageRectRight, cssPageRectBottom,
viewportRectLeft, viewportRectTop, viewportRectRight, viewportRectBottom,
left, top, right, bottom, zoomFactor);
}
public ImmutableViewportMetrics setFixedLayerMarginsFrom(ImmutableViewportMetrics fromMetrics) {
--- a/mobile/android/base/gfx/JavaPanZoomController.java
+++ b/mobile/android/base/gfx/JavaPanZoomController.java
@@ -64,16 +64,19 @@ class JavaPanZoomController
private static final float MAX_ZOOM = 4.0f;
// The maximum amount we would like to scroll with the mouse
private static final float MAX_SCROLL = 0.075f * GeckoAppShell.getDpi();
// The maximum zoom factor adjustment per frame of the AUTONAV animation
private static final float MAX_ZOOM_DELTA = 0.125f;
+ // Length of the bounce animation in ms
+ private static final int BOUNCE_ANIMATION_DURATION = 250;
+
private enum PanZoomState {
NOTHING, /* no touch-start events received */
FLING, /* all touches removed, but we're still scrolling page */
TOUCHING, /* one touch-start event received */
PANNING_LOCKED, /* touch-start followed by move (i.e. panning with axis lock) */
PANNING, /* panning without axis lock */
PANNING_HOLD, /* in panning, but not moving.
* similar to TOUCHING but after starting a pan */
@@ -803,31 +806,31 @@ class JavaPanZoomController
* out.
*/
if (!(mState == PanZoomState.BOUNCE || mState == PanZoomState.ANIMATED_ZOOM)) {
finishAnimation();
return;
}
/* Perform the next frame of the bounce-back animation. */
- if (mBounceFrame < (int)(256f/Axis.MS_PER_FRAME)) {
+ if (mBounceFrame < (int)(BOUNCE_ANIMATION_DURATION / Axis.MS_PER_FRAME)) {
advanceBounce();
return;
}
/* Finally, if there's nothing else to do, complete the animation and go to sleep. */
finishBounce();
finishAnimation();
setState(PanZoomState.NOTHING);
}
/* Performs one frame of a bounce animation. */
private void advanceBounce() {
synchronized (mTarget.getLock()) {
- float t = easeOut(mBounceFrame * Axis.MS_PER_FRAME / 256f);
+ float t = easeOut(mBounceFrame * Axis.MS_PER_FRAME / BOUNCE_ANIMATION_DURATION);
ImmutableViewportMetrics newMetrics = mBounceStartMetrics.interpolate(mBounceEndMetrics, t);
mTarget.setViewportMetrics(newMetrics);
mBounceFrame++;
}
}
/* Concludes a bounce animation and snaps the viewport into place. */
private void finishBounce() {
--- a/modules/libpref/src/init/all.js
+++ b/modules/libpref/src/init/all.js
@@ -326,16 +326,26 @@ pref("accessibility.tabfocus_applies_to_
//
// This pref is checked only once, and the browser needs a restart to
// pick up any changes.
//
// Values are -1 always on. 1 always off, 0 is auto as some platform perform
// further checks.
pref("accessibility.force_disabled", 0);
+#ifdef XP_WIN
+// Some accessibility tools poke at windows in the plugin process during setup
+// which can cause hangs. To hack around this set accessibility.delay_plugins
+// to true, you can also try increasing accessibility.delay_plugin_time if your
+// machine is slow and you still experience hangs.
+// See bug 781791.
+pref("accessibility.delay_plugins", false);
+pref("accessibility.delay_plugin_time", 10000);
+#endif
+
pref("focusmanager.testmode", false);
pref("accessibility.usetexttospeech", "");
pref("accessibility.usebrailledisplay", "");
pref("accessibility.accesskeycausesactivation", true);
pref("accessibility.mouse_focuses_formcontrol", false);
// Type Ahead Find
@@ -1697,21 +1707,17 @@ pref("layout.css.devPixelsPerPx", "-1.0"
// Is support for CSS Masking features enabled?
#ifdef RELEASE_BUILD
pref("layout.css.masking.enabled", false);
#else
pref("layout.css.masking.enabled", true);
#endif
// Is support for the the @supports rule enabled?
-#ifdef RELEASE_BUILD
-pref("layout.css.supports-rule.enabled", false);
-#else
pref("layout.css.supports-rule.enabled", true);
-#endif
// Is support for CSS Flexbox enabled?
pref("layout.css.flexbox.enabled", true);
// Are sets of prefixed properties supported?
pref("layout.css.prefixes.border-image", true);
pref("layout.css.prefixes.transforms", true);
pref("layout.css.prefixes.transitions", true);
--- a/netwerk/sctp/datachannel/DataChannel.cpp
+++ b/netwerk/sctp/datachannel/DataChannel.cpp
@@ -212,24 +212,29 @@ DataChannelConnection::Destroy()
// Though it's probably ok to do this and close the sockets;
// if we really want it to do true clean shutdowns it can
// create a dependant Internal object that would remain around
// until the network shut down the association or timed out.
LOG(("Destroying DataChannelConnection %p", (void *) this));
ASSERT_WEBRTC(NS_IsMainThread());
CloseAll();
+ MutexAutoLock lock(mLock);
if (mSocket && mSocket != mMasterSocket)
usrsctp_close(mSocket);
if (mMasterSocket)
usrsctp_close(mMasterSocket);
mSocket = nullptr;
- mMasterSocket = nullptr;
+ mMasterSocket = nullptr; // also a flag that we've Destroyed this connection
+ if (mUsingDtls) {
+ usrsctp_deregister_address(static_cast<void *>(this));
+ LOG(("Deregistered %p from the SCTP stack.", static_cast<void *>(this)));
+ }
// We can't get any more new callbacks from the SCTP library
// All existing callbacks have refs to DataChannelConnection
// nsDOMDataChannel objects have refs to DataChannels that have refs to us
if (mTransportFlow) {
MOZ_ASSERT(mSTS);
ASSERT_WEBRTC(NS_IsMainThread());
@@ -277,17 +282,17 @@ DataChannelConnection::Init(unsigned sho
#else
nullptr
#endif
);
#else
NS_ASSERTION(!aUsingDtls, "Trying to use SCTP/DTLS without mtransport");
#endif
} else {
- LOG(("sctp_init(%d)", aPort));
+ LOG(("sctp_init(%u)", aPort));
usrsctp_init(aPort,
nullptr,
#ifdef PR_LOGGING
debug_printf
#else
nullptr
#endif
);
@@ -295,16 +300,18 @@ DataChannelConnection::Init(unsigned sho
#ifdef PR_LOGGING
// Set logging to SCTP:PR_LOG_DEBUG to get SCTP debugs
if (PR_LOG_TEST(GetSCTPLog(), PR_LOG_ALWAYS)) {
usrsctp_sysctl_set_sctp_debug_on(SCTP_DEBUG_ALL);
}
#endif
usrsctp_sysctl_set_sctp_blackhole(2);
+ // ECN is currently not supported by the Firefox code
+ usrsctp_sysctl_set_sctp_ecn_enable(0);
sctp_initialized = true;
gDataChannelShutdown = new DataChannelShutdown();
gDataChannelShutdown->Init();
}
}
// XXX FIX! make this a global we get once
@@ -381,21 +388,19 @@ DataChannelConnection::Init(unsigned sho
event.se_type = event_types[i];
if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_EVENT, &event, sizeof(event)) < 0) {
LOG(("*** failed setsockopt SCTP_EVENT errno %d", errno));
goto error_cleanup;
}
}
// Update number of streams
- mStreamsOut.AppendElements(aNumStreams);
- mStreamsIn.AppendElements(aNumStreams); // make sure both are the same length
+ mStreams.AppendElements(aNumStreams);
for (uint32_t i = 0; i < aNumStreams; ++i) {
- mStreamsOut[i] = nullptr;
- mStreamsIn[i] = nullptr;
+ mStreams[i] = nullptr;
}
memset(&initmsg, 0, sizeof(initmsg));
len = sizeof(initmsg);
if (usrsctp_getsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_INITMSG, &initmsg, &len) < 0) {
LOG(("*** failed getsockopt SCTP_INITMSG"));
goto error_cleanup;
}
LOG(("Setting number of SCTP streams to %u, was %u/%u", aNumStreams,
@@ -404,21 +409,29 @@ DataChannelConnection::Init(unsigned sho
initmsg.sinit_max_instreams = MAX_NUM_STREAMS;
if (usrsctp_setsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_INITMSG, &initmsg,
(socklen_t)sizeof(initmsg)) < 0) {
LOG(("*** failed setsockopt SCTP_INITMSG, errno %d", errno));
goto error_cleanup;
}
mSocket = nullptr;
+ if (aUsingDtls) {
+ mUsingDtls = true;
+ usrsctp_register_address(static_cast<void *>(this));
+ LOG(("Registered %p within the SCTP stack.", static_cast<void *>(this)));
+ } else {
+ mUsingDtls = false;
+ }
return true;
error_cleanup:
usrsctp_close(mMasterSocket);
mMasterSocket = nullptr;
+ mUsingDtls = false;
return false;
}
void
DataChannelConnection::StartDefer()
{
nsresult rv;
if (!NS_IsMainThread()) {
@@ -467,78 +480,133 @@ DataChannelConnection::Notify(nsITimer *
LOG(("Turned off deferred send timer"));
mTimerRunning = false;
}
}
return NS_OK;
}
#ifdef MOZ_PEERCONNECTION
-bool
-DataChannelConnection::ConnectDTLS(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport)
+void
+DataChannelConnection::SetEvenOdd()
{
- LOG(("Connect DTLS local %d, remote %d", localport, remoteport));
+ ASSERT_WEBRTC(IsSTSThread());
- NS_PRECONDITION(mMasterSocket, "SCTP wasn't initialized before ConnectDTLS!");
+ TransportLayerDtls *dtls = static_cast<TransportLayerDtls *>(
+ mTransportFlow->GetLayer(TransportLayerDtls::ID()));
+ MOZ_ASSERT(dtls); // DTLS is mandatory
+ mAllocateEven = (dtls->role() == TransportLayerDtls::CLIENT);
+}
+
+bool
+DataChannelConnection::ConnectViaTransportFlow(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport)
+{
+ LOG(("Connect DTLS local %u, remote %u", localport, remoteport));
+
+ NS_PRECONDITION(mMasterSocket, "SCTP wasn't initialized before ConnectViaTransportFlow!");
NS_ENSURE_TRUE(aFlow, false);
mTransportFlow = aFlow;
- mTransportFlow->SignalPacketReceived.connect(this, &DataChannelConnection::SctpDtlsInput);
mLocalPort = localport;
mRemotePort = remoteport;
mState = CONNECTING;
+ RUN_ON_THREAD(mSTS, WrapRunnable(nsRefPtr<DataChannelConnection>(this),
+ &DataChannelConnection::SetSignals),
+ NS_DISPATCH_NORMAL);
+ return true;
+}
+
+void
+DataChannelConnection::SetSignals()
+{
+ ASSERT_WEBRTC(IsSTSThread());
+ ASSERT_WEBRTC(mTransportFlow);
+ LOG(("Setting transport signals, state: %d", mTransportFlow->state()));
+ mTransportFlow->SignalPacketReceived.connect(this, &DataChannelConnection::SctpDtlsInput);
+ // SignalStateChange() doesn't call you with the initial state
+ mTransportFlow->SignalStateChange.connect(this, &DataChannelConnection::CompleteConnect);
+ CompleteConnect(mTransportFlow, mTransportFlow->state());
+}
+
+void
+DataChannelConnection::CompleteConnect(TransportFlow *flow, TransportLayer::State state)
+{
+ LOG(("Data transport state: %d", state));
+ MutexAutoLock lock(mLock);
+ ASSERT_WEBRTC(IsSTSThread());
+ // We should abort connection on TS_ERROR.
+ // Note however that the association will also fail (perhaps with a delay) and
+ // notify us in that way
+ if (state != TransportLayer::TS_OPEN || !mMasterSocket)
+ return;
+
struct sockaddr_conn addr;
memset(&addr, 0, sizeof(addr));
addr.sconn_family = AF_CONN;
#if defined(__Userspace_os_Darwin)
addr.sconn_len = sizeof(addr);
#endif
addr.sconn_port = htons(mLocalPort);
+ addr.sconn_addr = static_cast<void *>(this);
LOG(("Calling usrsctp_bind"));
int r = usrsctp_bind(mMasterSocket, reinterpret_cast<struct sockaddr *>(&addr),
sizeof(addr));
if (r < 0) {
LOG(("usrsctp_bind failed: %d", r));
} else {
// This is the remote addr
addr.sconn_port = htons(mRemotePort);
- addr.sconn_addr = static_cast<void *>(this);
LOG(("Calling usrsctp_connect"));
r = usrsctp_connect(mMasterSocket, reinterpret_cast<struct sockaddr *>(&addr),
sizeof(addr));
if (r < 0) {
if (errno == EINPROGRESS) {
// non-blocking
- return true;
+ return;
} else {
LOG(("usrsctp_connect failed: %d", errno));
mState = CLOSED;
}
} else {
- // Notify Connection open
- LOG(("%s: sending ON_CONNECTION for %p", __FUNCTION__, this));
- mSocket = mMasterSocket;
- mState = OPEN;
- LOG(("DTLS connect() succeeded! Entering connected mode"));
-
- NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
- DataChannelOnMessageAvailable::ON_CONNECTION,
- this, true));
- return true;
+ // We set Even/Odd and fire ON_CONNECTION via SCTP_COMM_UP when we get that
+ // This also avoids issues with calling TransportFlow stuff on Mainthread
+ return;
}
}
// Note: currently this doesn't actually notify the application
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_CONNECTION,
this, false));
- return false;
+ return;
}
+// Process any pending Opens
+void
+DataChannelConnection::ProcessQueuedOpens()
+{
+ // Can't copy nsDeque's. Move into temp array since any that fail will
+ // go back to mPending
+ nsDeque temp;
+ DataChannel *temp_channel; // really already_AddRefed<>
+ while (nullptr != (temp_channel = static_cast<DataChannel *>(mPending.PopFront()))) {
+ temp.Push(static_cast<void *>(temp_channel));
+ }
+
+ nsRefPtr<DataChannel> channel;
+ while (nullptr != (channel = dont_AddRef(static_cast<DataChannel *>(temp.PopFront())))) {
+ if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) {
+ LOG(("Processing queued open for %p (%u)", channel.get(), channel->mStream));
+ channel->mFlags &= ~DATA_CHANNEL_FLAGS_FINISH_OPEN;
+ OpenFinish(channel.forget()); // may reset the flag and re-push
+ }
+ }
+
+}
void
DataChannelConnection::SctpDtlsInput(TransportFlow *flow,
const unsigned char *data, size_t len)
{
#ifdef PR_LOGGING
if (PR_LOG_TEST(GetSCTPLog(), PR_LOG_DEBUG)) {
char *buf;
@@ -600,35 +668,39 @@ DataChannelConnection::SctpDtlsOutput(vo
&DataChannelConnection::SendPacket, data, length, true),
NS_DISPATCH_NORMAL);
res = 0; // cheat! Packets can always be dropped later anyways
}
return res;
}
#endif
+#ifdef ALLOW_DIRECT_SCTP_LISTEN_CONNECT
// listen for incoming associations
// Blocks! - Don't call this from main thread!
+
+#error This code will not work as-is since SetEvenOdd() runs on Mainthread
+
bool
DataChannelConnection::Listen(unsigned short port)
{
struct sockaddr_in addr;
socklen_t addr_len;
NS_WARN_IF_FALSE(!NS_IsMainThread(), "Blocks, do not call from main thread!!!");
/* Acting as the 'server' */
memset((void *)&addr, 0, sizeof(addr));
#ifdef HAVE_SIN_LEN
addr.sin_len = sizeof(struct sockaddr_in);
#endif
addr.sin_family = AF_INET;
addr.sin_port = htons(port);
addr.sin_addr.s_addr = htonl(INADDR_ANY);
- LOG(("Waiting for connections on port %d", ntohs(addr.sin_port)));
+ LOG(("Waiting for connections on port %u", ntohs(addr.sin_port)));
mState = CONNECTING;
if (usrsctp_bind(mMasterSocket, reinterpret_cast<struct sockaddr *>(&addr), sizeof(struct sockaddr_in)) < 0) {
LOG(("***Failed userspace_bind"));
return false;
}
if (usrsctp_listen(mMasterSocket, 1) < 0) {
LOG(("***Failed userspace_listen"));
return false;
@@ -645,16 +717,18 @@ DataChannelConnection::Listen(unsigned s
struct linger l;
l.l_onoff = 1;
l.l_linger = 0;
if (usrsctp_setsockopt(mSocket, SOL_SOCKET, SO_LINGER,
(const void *)&l, (socklen_t)sizeof(struct linger)) < 0) {
LOG(("Couldn't set SO_LINGER on SCTP socket"));
}
+ SetEvenOdd();
+
// Notify Connection open
// XXX We need to make sure connection sticks around until the message is delivered
LOG(("%s: sending ON_CONNECTION for %p", __FUNCTION__, this));
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_CONNECTION,
this, (DataChannel *) nullptr));
return true;
}
@@ -721,81 +795,70 @@ DataChannelConnection::Connect(const cha
}
#endif
mSocket = mMasterSocket;
LOG(("connect() succeeded! Entering connected mode"));
mState = OPEN;
+ SetEvenOdd();
+
// Notify Connection open
// XXX We need to make sure connection sticks around until the message is delivered
LOG(("%s: sending ON_CONNECTION for %p", __FUNCTION__, this));
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_CONNECTION,
this, (DataChannel *) nullptr));
return true;
}
+#endif
DataChannel *
-DataChannelConnection::FindChannelByStreamIn(uint16_t streamIn)
+DataChannelConnection::FindChannelByStream(uint16_t streamOut)
{
- // Auto-extend mStreamsIn as needed
- if (((uint32_t) streamIn) + 1 > mStreamsIn.Length()) {
- uint32_t old_len = mStreamsIn.Length();
- LOG(("Extending mStreamsIn[] to %d elements", ((int32_t) streamIn)+1));
- mStreamsIn.AppendElements((streamIn+1) - mStreamsIn.Length());
- for (uint32_t i = old_len; i < mStreamsIn.Length(); ++i)
- mStreamsIn[i] = nullptr;
- }
- // Should always be safe in practice
- return mStreamsIn.SafeElementAt(streamIn);
-}
-
-DataChannel *
-DataChannelConnection::FindChannelByStreamOut(uint16_t streamOut)
-{
- return mStreamsOut.SafeElementAt(streamOut);
+ return mStreams.SafeElementAt(streamOut);
}
uint16_t
-DataChannelConnection::FindFreeStreamOut()
+DataChannelConnection::FindFreeStream()
{
uint32_t i, limit;
- limit = mStreamsOut.Length();
+ limit = mStreams.Length();
if (limit > MAX_NUM_STREAMS)
limit = MAX_NUM_STREAMS;
- for (i = 0; i < limit; ++i) {
- if (!mStreamsOut[i]) {
+
+ for (i = (mAllocateEven ? 0 : 1); i < limit; i += 2) {
+ if (!mStreams[i]) {
// Verify it's not still in the process of closing
for (uint32_t j = 0; j < mStreamsResetting.Length(); ++j) {
if (mStreamsResetting[j] == i) {
continue;
}
}
break;
}
}
if (i == limit) {
return INVALID_STREAM;
}
return i;
}
bool
-DataChannelConnection::RequestMoreStreamsOut(int32_t aNeeded)
+DataChannelConnection::RequestMoreStreams(int32_t aNeeded)
{
struct sctp_status status;
struct sctp_add_streams sas;
uint32_t outStreamsNeeded;
socklen_t len;
- if (aNeeded + mStreamsOut.Length() > MAX_NUM_STREAMS)
- aNeeded = MAX_NUM_STREAMS - mStreamsOut.Length();
+ if (aNeeded + mStreams.Length() > MAX_NUM_STREAMS)
+ aNeeded = MAX_NUM_STREAMS - mStreams.Length();
if (aNeeded <= 0)
return false;
len = (socklen_t)sizeof(struct sctp_status);
if (usrsctp_getsockopt(mMasterSocket, IPPROTO_SCTP, SCTP_STATUS, &status, &len) < 0) {
LOG(("***failed: getsockopt SCTP_STATUS"));
return false;
}
@@ -813,66 +876,44 @@ DataChannelConnection::RequestMoreStream
LOG(("***failed: setsockopt ADD errno=%d", errno));
return false;
}
LOG(("Requested %u more streams", outStreamsNeeded));
return true;
}
int32_t
-DataChannelConnection::SendControlMessage(void *msg, uint32_t len, uint16_t streamOut)
+DataChannelConnection::SendControlMessage(void *msg, uint32_t len, uint16_t stream)
{
struct sctp_sndinfo sndinfo;
// Note: Main-thread IO, but doesn't block
memset(&sndinfo, 0, sizeof(struct sctp_sndinfo));
- sndinfo.snd_sid = streamOut;
+ sndinfo.snd_sid = stream;
sndinfo.snd_ppid = htonl(DATA_CHANNEL_PPID_CONTROL);
if (usrsctp_sendv(mSocket, msg, len, nullptr, 0,
&sndinfo, (socklen_t)sizeof(struct sctp_sndinfo),
SCTP_SENDV_SNDINFO, 0) < 0) {
//LOG(("***failed: sctp_sendv")); don't log because errno is a return!
return (0);
}
return (1);
}
int32_t
-DataChannelConnection::SendOpenResponseMessage(uint16_t streamOut, uint16_t streamIn)
-{
- struct rtcweb_datachannel_open_response rsp;
-
- memset(&rsp, 0, sizeof(struct rtcweb_datachannel_open_response));
- rsp.msg_type = DATA_CHANNEL_OPEN_RESPONSE;
- rsp.reverse_stream = htons(streamIn);
-
- return SendControlMessage(&rsp, sizeof(rsp), streamOut);
-}
-
-
-int32_t
-DataChannelConnection::SendOpenAckMessage(uint16_t streamOut)
-{
- struct rtcweb_datachannel_ack ack;
-
- memset(&ack, 0, sizeof(struct rtcweb_datachannel_ack));
- ack.msg_type = DATA_CHANNEL_ACK;
-
- return SendControlMessage(&ack, sizeof(ack), streamOut);
-}
-
-int32_t
DataChannelConnection::SendOpenRequestMessage(const nsACString& label,
- uint16_t streamOut, bool unordered,
+ const nsACString& protocol,
+ uint16_t stream, bool unordered,
uint16_t prPolicy, uint32_t prValue)
{
- int len = label.Length(); // not including nul
+ int label_len = label.Length(); // not including nul
+ int proto_len = protocol.Length(); // not including nul
struct rtcweb_datachannel_open_request *req =
- (struct rtcweb_datachannel_open_request*) moz_xmalloc(sizeof(*req)+len);
- // careful - ok because request includes 1 char label
+ (struct rtcweb_datachannel_open_request*) moz_xmalloc((sizeof(*req)-1) + label_len + proto_len);
+ // careful - request includes 1 char label
memset(req, 0, sizeof(struct rtcweb_datachannel_open_request));
req->msg_type = DATA_CHANNEL_OPEN_REQUEST;
switch (prPolicy) {
case SCTP_PR_SCTP_NONE:
req->channel_type = DATA_CHANNEL_RELIABLE;
break;
case SCTP_PR_SCTP_TTL:
@@ -881,25 +922,30 @@ DataChannelConnection::SendOpenRequestMe
case SCTP_PR_SCTP_RTX:
req->channel_type = DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT;
break;
default:
// FIX! need to set errno! Or make all these SendXxxx() funcs return 0 or errno!
moz_free(req);
return (0);
}
- req->flags = htons(0);
if (unordered) {
- req->flags |= htons(DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED);
+ // Per the current types, all differ by 0x80 between ordered and unordered
+ req->channel_type |= 0x80; // NOTE: be careful if new types are added in the future
}
+
req->reliability_params = htons((uint16_t)prValue); /* XXX Why 16-bit */
req->priority = htons(0); /* XXX: add support */
- strcpy(&req->label[0], PromiseFlatCString(label).get());
+ req->label_length = label_len;
+ req->protocol_length = proto_len;
+ memcpy(&req->label[0], PromiseFlatCString(label).get(), label_len);
+ memcpy(&req->label[req->label_length], PromiseFlatCString(protocol).get(), proto_len);
- int32_t result = SendControlMessage(req, sizeof(*req)+len, streamOut);
+ // sizeof(*req) already includes +1 byte for label, need nul for both strings
+ int32_t result = SendControlMessage(req, (sizeof(*req)-1) + label_len + proto_len, stream);
moz_free(req);
return result;
}
// XXX This should use a separate thread (outbound queue) which should
// select() to know when to *try* to send data to the socket again.
// Alternatively, it can use a timeout, but that's guaranteed to be wrong
@@ -918,85 +964,46 @@ DataChannelConnection::SendDeferredMessa
bool still_blocked = false;
bool sent = false;
// This may block while something is modifying channels, but should not block for IO
MutexAutoLock lock(mLock);
// XXX For total fairness, on a still_blocked we'd start next time at the
// same index. Sorry, not going to bother for now.
- for (i = 0; i < mStreamsOut.Length(); ++i) {
- channel = mStreamsOut[i];
+ for (i = 0; i < mStreams.Length(); ++i) {
+ channel = mStreams[i];
if (!channel)
continue;
// Only one of these should be set....
if (channel->mFlags & DATA_CHANNEL_FLAGS_SEND_REQ) {
- if (SendOpenRequestMessage(channel->mLabel, channel->mStreamOut,
- channel->mFlags & DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED,
+ if (SendOpenRequestMessage(channel->mLabel, channel->mProtocol,
+ channel->mStream,
+ channel->mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED,
channel->mPrPolicy, channel->mPrValue)) {
channel->mFlags &= ~DATA_CHANNEL_FLAGS_SEND_REQ;
sent = true;
} else {
if (errno == EAGAIN || errno == EWOULDBLOCK) {
still_blocked = true;
} else {
// Close the channel, inform the user
- mStreamsOut[channel->mStreamOut] = nullptr;
+ mStreams[channel->mStream] = nullptr;
channel->mState = CLOSED;
// Don't need to reset; we didn't open it
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
channel));
}
}
}
if (still_blocked)
break;
- if (channel->mFlags & DATA_CHANNEL_FLAGS_SEND_RSP) {
- if (SendOpenResponseMessage(channel->mStreamOut, channel->mStreamIn)) {
- channel->mFlags &= ~DATA_CHANNEL_FLAGS_SEND_RSP;
- sent = true;
- } else {
- if (errno == EAGAIN || errno == EWOULDBLOCK) {
- still_blocked = true;
- } else {
- // Close the channel
- // Don't need to reset; we didn't open it
- // The other side may be left with a hanging Open. Our inability to
- // send the open response means we can't easily tell them about it
- // We haven't informed the user/DOM of the creation yet, so just
- // delete the channel.
- mStreamsIn[channel->mStreamIn] = nullptr;
- mStreamsOut[channel->mStreamOut] = nullptr;
- channel->mState = CLOSED;
- }
- }
- }
- if (still_blocked)
- break;
-
- if (channel->mFlags & DATA_CHANNEL_FLAGS_SEND_ACK) {
- if (SendOpenAckMessage(channel->mStreamOut)) {
- channel->mFlags &= ~DATA_CHANNEL_FLAGS_SEND_ACK;
- sent = true;
- } else {
- if (errno == EAGAIN || errno == EWOULDBLOCK) {
- still_blocked = true;
- } else {
- // Close the channel, inform the user
- CloseInt(channel);
- // XXX send error via DataChannelOnMessageAvailable (bug 843625)
- }
- }
- }
- if (still_blocked)
- break;
-
if (channel->mFlags & DATA_CHANNEL_FLAGS_SEND_DATA) {
bool failed_send = false;
int32_t result;
if (channel->mState == CLOSED || channel->mState == CLOSING) {
channel->mBufferedData.Clear();
}
while (!channel->mBufferedData.IsEmpty() &&
@@ -1047,247 +1054,190 @@ DataChannelConnection::SendDeferredMessa
mDeferTimeout--;
return true;
}
void
DataChannelConnection::HandleOpenRequestMessage(const struct rtcweb_datachannel_open_request *req,
size_t length,
- uint16_t streamIn)
+ uint16_t stream)
{
nsRefPtr<DataChannel> channel;
uint32_t prValue;
uint16_t prPolicy;
uint32_t flags;
- nsCString label(nsDependentCString(req->label));
mLock.AssertCurrentThreadOwns();
- if ((channel = FindChannelByStreamIn(streamIn))) {
- LOG(("ERROR: HandleOpenRequestMessage: channel for stream %d is in state %d instead of CLOSED.",
- streamIn, channel->mState));
- /* XXX: some error handling */
- return;
+ if (length != (sizeof(*req) - 1) + req->label_length + req->protocol_length) {
+ LOG(("Inconsistent length: %u, should be %u", length,
+ (sizeof(*req) - 1) + req->label_length + req->protocol_length));
+ if (length < (sizeof(*req) - 1) + req->label_length + req->protocol_length)
+ return;
}
+
switch (req->channel_type) {
case DATA_CHANNEL_RELIABLE:
+ case DATA_CHANNEL_RELIABLE_UNORDERED:
prPolicy = SCTP_PR_SCTP_NONE;
break;
case DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT:
+ case DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT_UNORDERED:
prPolicy = SCTP_PR_SCTP_RTX;
break;
case DATA_CHANNEL_PARTIAL_RELIABLE_TIMED:
+ case DATA_CHANNEL_PARTIAL_RELIABLE_TIMED_UNORDERED:
prPolicy = SCTP_PR_SCTP_TTL;
break;
default:
/* XXX error handling */
return;
}
prValue = ntohs(req->reliability_params);
- flags = ntohs(req->flags) & DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED;
+ flags = (req->channel_type & 0x80) ? DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED : 0;
+
+ if ((channel = FindChannelByStream(stream))) {
+ if (!(channel->mFlags & DATA_CHANNEL_FLAGS_EXTERNAL_NEGOTIATED)) {
+ LOG(("ERROR: HandleOpenRequestMessage: channel for stream %u is in state %d instead of CLOSED.",
+ stream, channel->mState));
+ /* XXX: some error handling */
+ } else {
+ LOG(("Open for externally negotiated channel %u", stream));
+ // XXX should also check protocol, maybe label
+ if (prPolicy != channel->mPrPolicy ||
+ prValue != channel->mPrValue ||
+ flags != (channel->mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED))
+ {
+ LOG(("WARNING: external negotiation mismatch with OpenRequest:"
+ "channel %u, policy %u/%u, value %u/%u, flags %x/%x",
+ stream, prPolicy, channel->mPrPolicy,
+ prValue, channel->mPrValue, flags, channel->mFlags));
+ }
+ }
+ return;
+ }
+
+ nsCString label(nsDependentCSubstring(&req->label[0], req->label_length));
+ nsCString protocol(nsDependentCSubstring(&req->label[req->label_length],
+ req->protocol_length));
+
channel = new DataChannel(this,
- INVALID_STREAM, streamIn,
+ stream,
DataChannel::CONNECTING,
label,
+ protocol,
prPolicy, prValue,
flags,
nullptr, nullptr);
- mStreamsIn[streamIn] = channel;
+ mStreams[stream] = channel;
+
+ channel->mState = DataChannel::WAITING_TO_OPEN;
- OpenResponseFinish(channel.forget());
+ LOG(("%s: sending ON_CHANNEL_CREATED for %s/%s: %u", __FUNCTION__,
+ channel->mLabel.get(), channel->mProtocol.get(), stream));
+ NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
+ DataChannelOnMessageAvailable::ON_CHANNEL_CREATED,
+ this, channel));
+
+ LOG(("%s: deferring sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel.get()));
+
+ // Now process any queued data messages for the channel (which will
+ // themselves likely get queued until we leave WAITING_TO_OPEN, plus any
+ // more that come in before that happens)
+ DeliverQueuedData(stream);
}
void
-DataChannelConnection::OpenResponseFinish(already_AddRefed<DataChannel> aChannel)
+DataChannelConnection::DeliverQueuedData(uint16_t stream)
{
- nsRefPtr<DataChannel> channel(aChannel);
- uint16_t streamOut = FindFreeStreamOut(); // may be INVALID_STREAM!
-
mLock.AssertCurrentThreadOwns();
- LOG(("Finished response: channel %p, streamOut = %u", channel.get(), streamOut));
-
- if (streamOut == INVALID_STREAM) {
- if (!RequestMoreStreamsOut()) {
- channel->mState = CLOSED;
- if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_RSP) {
- // We already returned the channel to the app.
- NS_ERROR("Failed to request more streams");
- NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
- DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
- channel));
- }
- // If we weren't deferred, we'll be destroying the channel, but it
- // never really got set up
- // Alternative would be to RUN_ON_THREAD(channel.forget(),::Destroy,...) and
- // Dispatch it to ourselves
- mStreamsIn[channel->mStreamIn] = nullptr;
- /* XXX: Signal error to the other end (and maybe fire onError: bug 843625) */
- return;
+ uint32_t i = 0;
+ while (i < mQueuedData.Length()) {
+ // Careful! we may modify the array length from within the loop!
+ if (mQueuedData[i]->mStream == stream) {
+ LOG(("Delivering queued data for stream %u, length %u",
+ stream, mQueuedData[i]->mLength));
+ // Deliver the queued data
+ HandleDataMessage(mQueuedData[i]->mPpid,
+ mQueuedData[i]->mData, mQueuedData[i]->mLength,
+ mQueuedData[i]->mStream);
+ mQueuedData.RemoveElementAt(i);
+ continue; // don't bump index since we removed the element
}
- LOG(("Queuing channel %d to finish response", channel->mStreamIn));
- channel->mFlags |= DATA_CHANNEL_FLAGS_FINISH_RSP;
- DataChannel *temp = channel.get(); // Can't cast away already_AddRefed<> from channel.forget()
- channel.forget();
- mPending.Push(temp);
- // can't notify the user until we can send an OpenResponse
- } else {
- channel->mStreamOut = streamOut;
- mStreamsOut[streamOut] = channel;
- if (SendOpenResponseMessage(streamOut, channel->mStreamIn)) {
- /* Notify ondatachannel */
- // XXX We need to make sure connection sticks around until the message is delivered
- LOG(("%s: sending ON_CHANNEL_CREATED for %s: %d/%d", __FUNCTION__,
- channel->mLabel.get(), streamOut, channel->mStreamIn));
- NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
- DataChannelOnMessageAvailable::ON_CHANNEL_CREATED,
- this, channel));
- } else {
- if (errno == EAGAIN || errno == EWOULDBLOCK) {
- channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_RSP;
- StartDefer();
- } else {
- if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_RSP) {
- // We already returned the channel to the app.
- NS_ERROR("Failed to send open response");
- NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
- DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
- channel));
- }
-
- /* XXX: Signal error to the other end. */
- mStreamsIn[channel->mStreamIn] = nullptr;
- mStreamsOut[streamOut] = nullptr;
- channel->mStreamOut = INVALID_STREAM;
- // we'll be destroying the channel if it wasn't already returned
- channel->mState = CLOSED;
- return;
- }
- }
- }
-}
-
-
-void
-DataChannelConnection::HandleOpenResponseMessage(const struct rtcweb_datachannel_open_response *rsp,
- size_t length, uint16_t streamIn)
-{
- uint16_t streamOut;
- DataChannel *channel;
-
- mLock.AssertCurrentThreadOwns();
-
- streamOut = ntohs(rsp->reverse_stream);
- channel = FindChannelByStreamOut(streamOut);
-
- NS_ENSURE_TRUE_VOID(channel);
- NS_ENSURE_TRUE_VOID(channel->mState == CONNECTING);
-
- if (rsp->error) {
- LOG(("%s: error in response to open of channel %d (%s)",
- __FUNCTION__, streamOut, channel->mLabel.get()));
-
- } else {
- NS_ENSURE_TRUE_VOID(!FindChannelByStreamIn(streamIn));
-
- channel->mStreamIn = streamIn;
- channel->mState = OPEN;
- channel->mReady = true;
- mStreamsIn[streamIn] = channel;
- if (SendOpenAckMessage(streamOut)) {
- channel->mFlags = 0;
- } else {
- // XXX Only on EAGAIN!? And if not, then close the channel??
- channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_ACK;
- StartDefer();
- }
- LOG(("%s: sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel));
- NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
- DataChannelOnMessageAvailable::ON_CHANNEL_OPEN, this,
- channel));
+ i++;
}
}
void
-DataChannelConnection::HandleOpenAckMessage(const struct rtcweb_datachannel_ack *ack,
- size_t length, uint16_t streamIn)
-{
- DataChannel *channel;
-
- mLock.AssertCurrentThreadOwns();
-
- channel = FindChannelByStreamIn(streamIn);
-
- NS_ENSURE_TRUE_VOID(channel);
- NS_ENSURE_TRUE_VOID(channel->mState == CONNECTING);
-
- channel->mState = channel->mReady ? DataChannel::OPEN : DataChannel::WAITING_TO_OPEN;
- if (channel->mState == OPEN) {
- LOG(("%s: sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel));
- NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
- DataChannelOnMessageAvailable::ON_CHANNEL_OPEN, this,
- channel));
- } else {
- LOG(("%s: deferring sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel));
- }
-}
-
-void
-DataChannelConnection::HandleUnknownMessage(uint32_t ppid, size_t length, uint16_t streamIn)
+DataChannelConnection::HandleUnknownMessage(uint32_t ppid, size_t length, uint16_t stream)
{
/* XXX: Send an error message? */
- LOG(("unknown DataChannel message received: %u, len %ld on stream %lu", ppid, length, streamIn));
+ LOG(("unknown DataChannel message received: %u, len %ld on stream %lu", ppid, length, stream));
// XXX Log to JS error console if possible
}
void
DataChannelConnection::HandleDataMessage(uint32_t ppid,
const void *data, size_t length,
- uint16_t streamIn)
+ uint16_t stream)
{
DataChannel *channel;
const char *buffer = (const char *) data;
mLock.AssertCurrentThreadOwns();
- channel = FindChannelByStreamIn(streamIn);
+ channel = FindChannelByStream(stream);
// XXX A closed channel may trip this... check
- NS_ENSURE_TRUE_VOID(channel);
- NS_ENSURE_TRUE_VOID(channel->mState != CONNECTING);
+ if (!channel) {
+ // In the updated 0-RTT open case, the sender can send data immediately
+ // after Open, and doesn't set the in-order bit (since we don't have a
+ // response or ack). Also, with external negotiation, data can come in
+ // before we're told about the external negotiation. We need to buffer
+ // data until either a) Open comes in, if the ordering get messed up,
+ // or b) the app tells us this channel was externally negotiated. When
+ // these occur, we deliver the data.
+
+ // Since this is rare and non-performance, keep a single list of queued
+ // data messages to deliver once the channel opens.
+ LOG(("Queuing data for stream %u, length %u", stream, length));
+ mQueuedData.AppendElement(new QueuedDataMessage(stream, ppid, data, length));
+ return;
+ }
// XXX should this be a simple if, no warnings/debugbreaks?
NS_ENSURE_TRUE_VOID(channel->mState != CLOSED);
{
nsAutoCString recvData(buffer, length);
switch (ppid) {
case DATA_CHANNEL_PPID_DOMSTRING:
- LOG(("DataChannel: String message received of length %lu on channel %d: %.*s",
- length, channel->mStreamOut, (int)PR_MIN(length, 80), buffer));
+ LOG(("DataChannel: String message received of length %lu on channel %u: %.*s",
+ length, channel->mStream, (int)PR_MIN(length, 80), buffer));
length = -1; // Flag for DOMString
// WebSockets checks IsUTF8() here; we can try to deliver it
NS_WARN_IF_FALSE(channel->mBinaryBuffer.IsEmpty(), "Binary message aborted by text message!");
if (!channel->mBinaryBuffer.IsEmpty())
channel->mBinaryBuffer.Truncate(0);
break;
case DATA_CHANNEL_PPID_BINARY:
channel->mBinaryBuffer += recvData;
- LOG(("DataChannel: Received binary message of length %lu (total %u) on channel id %d",
- length, channel->mBinaryBuffer.Length(), channel->mStreamOut));
+ LOG(("DataChannel: Received binary message of length %lu (total %u) on channel id %u",
+ length, channel->mBinaryBuffer.Length(), channel->mStream));
return; // Not ready to notify application
case DATA_CHANNEL_PPID_BINARY_LAST:
- LOG(("DataChannel: Received binary message of length %lu on channel id %d",
- length, channel->mStreamOut));
+ LOG(("DataChannel: Received binary message of length %lu on channel id %u",
+ length, channel->mStream));
if (!channel->mBinaryBuffer.IsEmpty()) {
channel->mBinaryBuffer += recvData;
LOG(("%s: sending ON_DATA (binary fragmented) for %p", __FUNCTION__, channel));
channel->SendOrQueue(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_DATA, this,
channel, channel->mBinaryBuffer,
channel->mBinaryBuffer.Length()));
channel->mBinaryBuffer.Truncate(0);
@@ -1305,82 +1255,73 @@ DataChannelConnection::HandleDataMessage
channel->SendOrQueue(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_DATA, this,
channel, recvData, length));
}
}
// Called with mLock locked!
void
-DataChannelConnection::HandleMessage(const void *buffer, size_t length, uint32_t ppid, uint16_t streamIn)
+DataChannelConnection::HandleMessage(const void *buffer, size_t length, uint32_t ppid, uint16_t stream)
{
const struct rtcweb_datachannel_open_request *req;
- const struct rtcweb_datachannel_open_response *rsp;
- const struct rtcweb_datachannel_ack *ack, *msg;
mLock.AssertCurrentThreadOwns();
switch (ppid) {
case DATA_CHANNEL_PPID_CONTROL:
- NS_ENSURE_TRUE_VOID(length >= sizeof(*ack)); // Ack is the smallest
+ NS_ENSURE_TRUE_VOID(length >= sizeof(*req));
- msg = static_cast<const struct rtcweb_datachannel_ack *>(buffer);
- switch (msg->msg_type) {
+ req = static_cast<const struct rtcweb_datachannel_open_request *>(buffer);
+ switch (req->msg_type) {
case DATA_CHANNEL_OPEN_REQUEST:
LOG(("length %u, sizeof(*req) = %u", length, sizeof(*req)));
NS_ENSURE_TRUE_VOID(length >= sizeof(*req));
- req = static_cast<const struct rtcweb_datachannel_open_request *>(buffer);
- HandleOpenRequestMessage(req, length, streamIn);
- break;
- case DATA_CHANNEL_OPEN_RESPONSE:
- NS_ENSURE_TRUE_VOID(length >= sizeof(*rsp));
-
- rsp = static_cast<const struct rtcweb_datachannel_open_response *>(buffer);
- HandleOpenResponseMessage(rsp, length, streamIn);
- break;
- case DATA_CHANNEL_ACK:
- // >= sizeof(*ack) checked above
-
- ack = static_cast<const struct rtcweb_datachannel_ack *>(buffer);
- HandleOpenAckMessage(ack, length, streamIn);
+ HandleOpenRequestMessage(req, length, stream);
break;
default:
- HandleUnknownMessage(ppid, length, streamIn);
+ HandleUnknownMessage(ppid, length, stream);
break;
}
break;
case DATA_CHANNEL_PPID_DOMSTRING:
case DATA_CHANNEL_PPID_BINARY:
case DATA_CHANNEL_PPID_BINARY_LAST:
- HandleDataMessage(ppid, buffer, length, streamIn);
+ HandleDataMessage(ppid, buffer, length, stream);
break;
default:
LOG(("Message of length %lu, PPID %u on stream %u received.",
- length, ppid, streamIn));
+ length, ppid, stream));
break;
}
}
void
DataChannelConnection::HandleAssociationChangeEvent(const struct sctp_assoc_change *sac)
{
uint32_t i, n;
switch (sac->sac_state) {
case SCTP_COMM_UP:
LOG(("Association change: SCTP_COMM_UP"));
if (mState == CONNECTING) {
mSocket = mMasterSocket;
mState = OPEN;
+ SetEvenOdd();
+
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_CONNECTION,
this, true));
LOG(("DTLS connect() succeeded! Entering connected mode"));
+
+ // Open any streams pending...
+ ProcessQueuedOpens();
+
} else if (mState == OPEN) {
LOG(("DataConnection Already OPEN"));
} else {
LOG(("Unexpected state: %d", mState));
}
break;
case SCTP_COMM_LOST:
LOG(("Association change: SCTP_COMM_LOST"));
@@ -1548,32 +1489,32 @@ DataChannelConnection::HandleSendFailedE
LOG(("Unsent "));
}
if (ssfe->ssfe_flags & SCTP_DATA_SENT) {
LOG(("Sent "));
}
if (ssfe->ssfe_flags & ~(SCTP_DATA_SENT | SCTP_DATA_UNSENT)) {
LOG(("(flags = %x) ", ssfe->ssfe_flags));
}
- LOG(("message with PPID = %d, SID = %d, flags: 0x%04x due to error = 0x%08x",
+ LOG(("message with PPID = %u, SID = %d, flags: 0x%04x due to error = 0x%08x",
ntohl(ssfe->ssfe_info.snd_ppid), ssfe->ssfe_info.snd_sid,
ssfe->ssfe_info.snd_flags, ssfe->ssfe_error));
n = ssfe->ssfe_length - sizeof(struct sctp_send_failed_event);
for (i = 0; i < n; ++i) {
LOG((" 0x%02x", ssfe->ssfe_data[i]));
}
}
void
DataChannelConnection::ResetOutgoingStream(uint16_t streamOut)
{
uint32_t i;
mLock.AssertCurrentThreadOwns();
- LOG(("Connection %p: Resetting outgoing stream %d",
+ LOG(("Connection %p: Resetting outgoing stream %u",
(void *) this, streamOut));
// Rarely has more than a couple items and only for a short time
for (i = 0; i < mStreamsResetting.Length(); ++i) {
if (mStreamsResetting[i] == streamOut) {
return;
}
}
mStreamsResetting.AppendElement(streamOut);
@@ -1615,65 +1556,62 @@ DataChannelConnection::HandleStreamReset
uint32_t n, i;
nsRefPtr<DataChannel> channel; // since we may null out the ref to the channel
if (!(strrst->strreset_flags & SCTP_STREAM_RESET_DENIED) &&
!(strrst->strreset_flags & SCTP_STREAM_RESET_FAILED)) {
n = (strrst->strreset_length - sizeof(struct sctp_stream_reset_event)) / sizeof(uint16_t);
for (i = 0; i < n; ++i) {
if (strrst->strreset_flags & SCTP_STREAM_RESET_INCOMING_SSN) {
- channel = FindChannelByStreamIn(strrst->strreset_stream_list[i]);
+ channel = FindChannelByStream(strrst->strreset_stream_list[i]);
if (channel) {
// The other side closed the channel
// We could be in three states:
// 1. Normal state (input and output streams (OPEN)
// Notify application, send a RESET in response on our
// outbound channel. Go to CLOSED
// 2. We sent our own reset (CLOSING); either they crossed on the
// wire, or this is a response to our Reset.
// Go to CLOSED
// 3. We've sent a open but haven't gotten a response yet (OPENING)
// I believe this is impossible, as we don't have an input stream yet.
- LOG(("Incoming: Channel %d outgoing/%d incoming closed, state %d",
- channel->mStreamOut, channel->mStreamIn, channel->mState));
+ LOG(("Incoming: Channel %u closed, state %d",
+ channel->mStream, channel->mState));
ASSERT_WEBRTC(channel->mState == DataChannel::OPEN ||
channel->mState == DataChannel::CLOSING ||
channel->mState == DataChannel::WAITING_TO_OPEN);
if (channel->mState == DataChannel::OPEN ||
channel->mState == DataChannel::WAITING_TO_OPEN) {
- ResetOutgoingStream(channel->mStreamOut);
+ ResetOutgoingStream(channel->mStream);
SendOutgoingStreamReset();
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
channel));
- mStreamsOut[channel->mStreamOut] = nullptr;
}
- mStreamsIn[channel->mStreamIn] = nullptr;
+ mStreams[channel->mStream] = nullptr;
LOG(("Disconnected DataChannel %p from connection %p",
(void *) channel.get(), (void *) channel->mConnection.get()));
channel->Destroy();
// At this point when we leave here, the object is a zombie held alive only by the DOM object
} else {
LOG(("Can't find incoming channel %d",i));
}
}
if (strrst->strreset_flags & SCTP_STREAM_RESET_OUTGOING_SSN) {
- channel = FindChannelByStreamOut(strrst->strreset_stream_list[i]);
+ channel = FindChannelByStream(strrst->strreset_stream_list[i]);
if (channel) {
- LOG(("Outgoing: Connection %p channel %p streams: %d outgoing/%d incoming closed",
- (void *) this, (void *) channel.get(), channel->mStreamOut, channel->mStreamIn));
+ LOG(("Outgoing: Connection %p channel %p stream: %u closed",
+ (void *) this, (void *) channel.get(), channel->mStream));
ASSERT_WEBRTC(channel->mState == CLOSING);
if (channel->mState == CLOSING) {
- mStreamsOut[channel->mStreamOut] = nullptr;
- if (channel->mStreamIn != INVALID_STREAM)
- mStreamsIn[channel->mStreamIn] = nullptr;
+ mStreams[channel->mStream] = nullptr;
LOG(("Disconnected DataChannel %p from connection %p (refcnt will be %u)",
(void *) channel.get(), (void *) channel->mConnection.get(),
(uint32_t) channel->mConnection->mRefCnt-1));
channel->Destroy();
// At this point when we leave here, the object is a zombie held alive only by the DOM object
}
} else {
LOG(("Can't find outgoing channel %d",i));
@@ -1681,112 +1619,89 @@ DataChannelConnection::HandleStreamReset
}
}
}
}
void
DataChannelConnection::HandleStreamChangeEvent(const struct sctp_stream_change_event *strchg)
{
- uint16_t streamOut;
+ uint16_t stream;
uint32_t i;
nsRefPtr<DataChannel> channel;
if (strchg->strchange_flags == SCTP_STREAM_CHANGE_DENIED) {
LOG(("*** Failed increasing number of streams from %u (%u/%u)",
- mStreamsOut.Length(),
+ mStreams.Length(),
strchg->strchange_instrms,
strchg->strchange_outstrms));
// XXX FIX! notify pending opens of failure
return;
} else {
- if (strchg->strchange_instrms > mStreamsIn.Length()) {
+ if (strchg->strchange_instrms > mStreams.Length()) {
LOG(("Other side increased streamds from %u to %u",
- mStreamsIn.Length(), strchg->strchange_instrms));
+ mStreams.Length(), strchg->strchange_instrms));
}
- if (strchg->strchange_outstrms > mStreamsOut.Length()) {
- uint16_t old_len = mStreamsOut.Length();
+ if (strchg->strchange_outstrms > mStreams.Length()) {
+ uint16_t old_len = mStreams.Length();
LOG(("Increasing number of streams from %u to %u - adding %u (in: %u)",
old_len,
strchg->strchange_outstrms,
strchg->strchange_outstrms - old_len,
strchg->strchange_instrms));
// make sure both are the same length
- mStreamsOut.AppendElements(strchg->strchange_outstrms - old_len);
- LOG(("New length = %d (was %d)", mStreamsOut.Length(), old_len));
- for (uint32_t i = old_len; i < mStreamsOut.Length(); ++i) {
- mStreamsOut[i] = nullptr;
+ mStreams.AppendElements(strchg->strchange_outstrms - old_len);
+ LOG(("New length = %d (was %d)", mStreams.Length(), old_len));
+ for (uint32_t i = old_len; i < mStreams.Length(); ++i) {
+ mStreams[i] = nullptr;
}
// Re-process any channels waiting for streams.
// Linear search, but we don't increase channels often and
// the array would only get long in case of an app error normally
// Make sure we request enough streams if there's a big jump in streams
// Could make a more complex API for OpenXxxFinish() and avoid this loop
int32_t num_needed = mPending.GetSize();
LOG(("%d of %d new streams already needed", num_needed,
strchg->strchange_outstrms - old_len));
num_needed -= (strchg->strchange_outstrms - old_len); // number we added
if (num_needed > 0) {
if (num_needed < 16)
num_needed = 16;
LOG(("Not enough new streams, asking for %d more", num_needed));
- RequestMoreStreamsOut(num_needed);
- }
-
- // Can't copy nsDeque's. Move into temp array since any that fail will
- // go back to mPending
- nsDeque temp;
- DataChannel *temp_channel; // really already_AddRefed<>
- while (nullptr != (temp_channel = static_cast<DataChannel *>(mPending.PopFront()))) {
- temp.Push(static_cast<void *>(temp_channel));
+ RequestMoreStreams(num_needed);
}
- // Now assign our new streams
- while (nullptr != (channel = dont_AddRef(static_cast<DataChannel *>(temp.PopFront())))) {
- if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_RSP) {
- channel->mFlags &= ~DATA_CHANNEL_FLAGS_FINISH_RSP;
- OpenResponseFinish(channel.forget()); // may reset the flag and re-push
- } else if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) {
- channel->mFlags &= ~DATA_CHANNEL_FLAGS_FINISH_OPEN;
- OpenFinish(channel.forget()); // may reset the flag and re-push
- }
- }
+ ProcessQueuedOpens();
}
// else probably not a change in # of streams
}
- for (i = 0; i < mStreamsOut.Length(); ++i) {
- channel = mStreamsOut[i];
+ for (i = 0; i < mStreams.Length(); ++i) {
+ channel = mStreams[i];
if (!channel)
continue;
if ((channel->mState == CONNECTING) &&
- (channel->mStreamOut == INVALID_STREAM)) {
+ (channel->mStream == INVALID_STREAM)) {
if ((strchg->strchange_flags & SCTP_STREAM_CHANGE_DENIED) ||
(strchg->strchange_flags & SCTP_STREAM_CHANGE_FAILED)) {
/* XXX: Signal to the other end. */
- if (channel->mStreamIn != INVALID_STREAM) {
- mStreamsIn[channel->mStreamIn] = nullptr;
- }
channel->mState = CLOSED;
NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
channel));
// maybe fire onError (bug 843625)
} else {
- streamOut = FindFreeStreamOut();
- if (streamOut != INVALID_STREAM) {
- channel->mStreamOut = streamOut;
- mStreamsOut[streamOut] = channel;
- if (channel->mStreamIn == INVALID_STREAM) {
- channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_REQ;
- } else {
- channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_RSP;
- }
+ stream = FindFreeStream();
+ if (stream != INVALID_STREAM) {
+ channel->mStream = stream;
+ mStreams[stream] = channel;
+ channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_REQ;
+ /// XXX fix
StartDefer();
} else {
/* We will not find more ... */
break;
}
}
}
}
@@ -1863,137 +1778,193 @@ DataChannelConnection::ReceiveCallback(s
HandleMessage(data, datalen, ntohl(rcv.rcv_ppid), rcv.rcv_sid);
}
}
// usrsctp defines the callback as returning an int, but doesn't use it
return 1;
}
already_AddRefed<DataChannel>
-DataChannelConnection::Open(const nsACString& label, Type type, bool inOrder,
+DataChannelConnection::Open(const nsACString& label, const nsACString& protocol,
+ Type type, bool inOrder,
uint32_t prValue, DataChannelListener *aListener,
- nsISupports *aContext)
+ nsISupports *aContext, bool aExternalNegotiated,
+ uint16_t aStream)
{
+ // aStream == INVALID_STREAM to have the protocol allocate
uint16_t prPolicy = SCTP_PR_SCTP_NONE;
uint32_t flags;
- LOG(("DC Open: label %s, type %u, inorder %d, prValue %u, listener %p, context %p",
- PromiseFlatCString(label).get(), type, inOrder, prValue, aListener, aContext));
+ LOG(("DC Open: label %s/%s, type %u, inorder %d, prValue %u, listener %p, context %p, external: %s, stream %u",
+ PromiseFlatCString(label).get(), PromiseFlatCString(protocol).get(),
+ type, inOrder, prValue, aListener, aContext,
+ aExternalNegotiated ? "true" : "false", aStream));
switch (type) {
case DATA_CHANNEL_RELIABLE:
prPolicy = SCTP_PR_SCTP_NONE;
break;
case DATA_CHANNEL_PARTIAL_RELIABLE_REXMIT:
prPolicy = SCTP_PR_SCTP_RTX;
break;
case DATA_CHANNEL_PARTIAL_RELIABLE_TIMED:
prPolicy = SCTP_PR_SCTP_TTL;
break;
}
if ((prPolicy == SCTP_PR_SCTP_NONE) && (prValue != 0)) {
return nullptr;
}
- flags = !inOrder ? DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED : 0;
+ if (aStream != INVALID_STREAM && mStreams[aStream]) {
+ LOG(("ERROR: external negotiation of already-open channel %u", aStream));
+ // XXX How do we indicate this up to the application? Probably the
+ // caller's job, but we may need to return an error code.
+ return nullptr;
+ }
+
+ flags = !inOrder ? DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED : 0;
nsRefPtr<DataChannel> channel(new DataChannel(this,
- INVALID_STREAM, INVALID_STREAM,
+ aStream,
DataChannel::CONNECTING,
- label, type, prValue,
+ label, protocol,
+ type, prValue,
flags,
aListener, aContext));
+ if (aExternalNegotiated) {
+ channel->mFlags |= DATA_CHANNEL_FLAGS_EXTERNAL_NEGOTIATED;
+ }
MutexAutoLock lock(mLock); // OpenFinish assumes this
return OpenFinish(channel.forget());
}
// Separate routine so we can also call it to finish up from pending opens
already_AddRefed<DataChannel>
DataChannelConnection::OpenFinish(already_AddRefed<DataChannel> aChannel)
{
- uint16_t streamOut = FindFreeStreamOut(); // may be INVALID_STREAM!
nsRefPtr<DataChannel> channel(aChannel);
+ uint16_t stream = channel->mStream;
mLock.AssertCurrentThreadOwns();
- LOG(("Finishing open: channel %p, streamOut = %u", channel.get(), streamOut));
+ if (stream == INVALID_STREAM || mState != OPEN) {
+ if (mState == OPEN) { // implies INVALID_STREAM
+ // Don't try to find a stream if not open - mAllocateEven isn't set yet
+ stream = FindFreeStream(); // may be INVALID_STREAM if we need more
+ if (stream == INVALID_STREAM) {
+ if (!RequestMoreStreams()) {
+ channel->mState = CLOSED;
+ if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) {
+ // We already returned the channel to the app.
+ NS_ERROR("Failed to request more streams");
+ NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
+ DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
+ channel));
+ return channel.forget();
+ }
+ // we'll be destroying the channel, but it never really got set up
+ // Alternative would be to RUN_ON_THREAD(channel.forget(),::Destroy,...) and
+ // Dispatch it to ourselves
+ return nullptr;
+ }
+ }
+ // if INVALID here, we need to queue
+ }
+ if (stream != INVALID_STREAM) {
+ // just allocated (& OPEN), or externally negotiated
+ mStreams[stream] = channel;
+ channel->mStream = stream;
+ }
- if (streamOut == INVALID_STREAM) {
- if (!RequestMoreStreamsOut()) {
- channel->mState = CLOSED;
- if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) {
- // We already returned the channel to the app.
- NS_ERROR("Failed to request more streams");
- NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
- DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
- channel));
- return channel.forget();
- }
- // we'll be destroying the channel, but it never really got set up
- // Alternative would be to RUN_ON_THREAD(channel.forget(),::Destroy,...) and
- // Dispatch it to ourselves
- return nullptr;
- }
- LOG(("Queuing channel %p to finish open", channel.get()));
- // Also serves to mark we told the app
- channel->mFlags |= DATA_CHANNEL_FLAGS_FINISH_OPEN;
- channel->AddRef(); // we need a ref for the nsDeQue and one to return
- mPending.Push(channel);
- return channel.forget();
+ LOG(("Finishing open: channel %p, stream = %u", channel.get(), stream));
+
+ if (stream == INVALID_STREAM || mState != OPEN) {
+ // we're going to queue
+
+ LOG(("Queuing channel %p (%u) to finish open", channel.get(), stream));
+ // Also serves to mark we told the app
+ channel->mFlags |= DATA_CHANNEL_FLAGS_FINISH_OPEN;
+ channel->AddRef(); // we need a ref for the nsDeQue and one to return
+ mPending.Push(channel);
+ return channel.forget();
+ } // else OPEN and we selected a stream
+ } else {
+ // OPEN and externally negotiated stream
+ mStreams[stream] = channel;
}
- mStreamsOut[streamOut] = channel;
- channel->mStreamOut = streamOut;
+
+#ifdef TEST_QUEUED_DATA
+ // It's painful to write a test for this...
+ channel->mState = OPEN;
+ channel->mReady = true;
+ SendMsgInternal(channel, "Help me!", 8, DATA_CHANNEL_PPID_DOMSTRING);
+#endif
+
+ if (!(channel->mFlags & DATA_CHANNEL_FLAGS_EXTERNAL_NEGOTIATED)) {
+ if (!SendOpenRequestMessage(channel->mLabel, channel->mProtocol,
+ stream,
+ !!(channel->mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED),
+ channel->mPrPolicy, channel->mPrValue)) {
+ LOG(("SendOpenRequest failed, errno = %d", errno));
+ if (errno == EAGAIN || errno == EWOULDBLOCK) {
+ channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_REQ;
+ StartDefer();
- if (!SendOpenRequestMessage(channel->mLabel, streamOut,
- !!(channel->mFlags & DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED),
- channel->mPrPolicy, channel->mPrValue)) {
- LOG(("SendOpenRequest failed, errno = %d", errno));
- if (errno == EAGAIN || errno == EWOULDBLOCK) {
- channel->mFlags |= DATA_CHANNEL_FLAGS_SEND_REQ;
- StartDefer();
- } else {
- if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) {
- // We already returned the channel to the app.
- NS_ERROR("Failed to send open request");
- NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
- DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
- channel));
+ return channel.forget();
+ } else {
+ if (channel->mFlags & DATA_CHANNEL_FLAGS_FINISH_OPEN) {
+ // We already returned the channel to the app.
+ NS_ERROR("Failed to send open request");
+ NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
+ DataChannelOnMessageAvailable::ON_CHANNEL_CLOSED, this,
+ channel));
+ }
+ // If we haven't returned the channel yet, it will get destroyed when we exit
+ // this function.
+ mStreams[stream] = nullptr;
+ channel->mStream = INVALID_STREAM;
+ // we'll be destroying the channel
+ channel->mState = CLOSED;
+ return nullptr;
}
- // If we haven't returned the channel yet, it will get destroyed when we exit
- // this function.
- mStreamsOut[streamOut] = nullptr;
- channel->mStreamOut = INVALID_STREAM;
- // we'll be destroying the channel
- channel->mState = CLOSED;
- return nullptr;
+ /* NOTREACHED */
}
}
+ // Either externally negotiated or we sent Open
+ channel->mState = OPEN;
+ channel->mReady = true;
+ // FIX? Move into DOMDataChannel? I don't think we can send it yet here
+ LOG(("%s: sending ON_CHANNEL_OPEN for %p", __FUNCTION__, channel.get()));
+ NS_DispatchToMainThread(new DataChannelOnMessageAvailable(
+ DataChannelOnMessageAvailable::ON_CHANNEL_OPEN, this,
+ channel));
+
return channel.forget();
}
int32_t
DataChannelConnection::SendMsgInternal(DataChannel *channel, const char *data,
uint32_t length, uint32_t ppid)
{
uint16_t flags;
struct sctp_sendv_spa spa;
int32_t result;
NS_ENSURE_TRUE(channel->mState == OPEN || channel->mState == CONNECTING, 0);
NS_WARN_IF_FALSE(length > 0, "Length is 0?!");
- flags = (channel->mFlags & DATA_CHANNEL_FLAG_OUT_OF_ORDER_ALLOWED) ? SCTP_UNORDERED : 0;
+ flags = (channel->mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED) ? SCTP_UNORDERED : 0;
// To avoid problems where an in-order OPEN_RESPONSE is lost and an
// out-of-order data message "beats" it, require data to be in-order
// until we get an ACK.
if (channel->mState == CONNECTING) {
flags &= ~SCTP_UNORDERED;
}
spa.sendv_sndinfo.snd_ppid = htonl(ppid);
- spa.sendv_sndinfo.snd_sid = channel->mStreamOut;
+ spa.sendv_sndinfo.snd_sid = channel->mStream;
spa.sendv_sndinfo.snd_flags = flags;
spa.sendv_sndinfo.snd_context = 0;
spa.sendv_sndinfo.snd_assoc_id = 0;
spa.sendv_flags = SCTP_SEND_SNDINFO_VALID;
if (channel->mPrPolicy != SCTP_PR_SCTP_NONE) {
spa.sendv_prinfo.pr_policy = channel->mPrPolicy;
spa.sendv_prinfo.pr_value = channel->mPrValue;
@@ -2046,28 +2017,30 @@ DataChannelConnection::SendBinary(DataCh
// large message. On an unreliable channel, we can't and don't know how
// long to wait, and there are no retransmissions, and no easy way to
// tell the user "this part is missing", so on unreliable channels we
// need to return an error if sending more bytes than the network buffers
// can hold, and perhaps a lower number.
// We *really* don't want to do this from main thread! - and SendMsgInternal
// avoids blocking.
+ // This MUST be reliable and in-order for the reassembly to work
if (len > DATA_CHANNEL_MAX_BINARY_FRAGMENT &&
- channel->mPrPolicy == DATA_CHANNEL_RELIABLE) {
+ channel->mPrPolicy == DATA_CHANNEL_RELIABLE &&
+ !(channel->mFlags & DATA_CHANNEL_FLAGS_OUT_OF_ORDER_ALLOWED)) {
int32_t sent=0;
uint32_t origlen = len;
LOG(("Sending binary message length %u in chunks", len));
// XXX check flags for out-of-order, or force in-order for large binary messages
while (len > 0) {
uint32_t sendlen = PR_MIN(len, DATA_CHANNEL_MAX_BINARY_FRAGMENT);
uint32_t ppid;
len -= sendlen;
ppid = len > 0 ? DATA_CHANNEL_PPID_BINARY : DATA_CHANNEL_PPID_BINARY_LAST;
- LOG(("Send chunk of %d bytes, ppid %d", sendlen, ppid));
+ LOG(("Send chunk of %u bytes, ppid %u", sendlen, ppid));
// Note that these might end up being deferred and queued.
sent += SendMsgInternal(channel, data, sendlen, ppid);
data += sendlen;
}
LOG(("Sent %d buffers for %u bytes, %d sent immediately, %d buffers queued",
(origlen+DATA_CHANNEL_MAX_BINARY_FRAGMENT-1)/DATA_CHANNEL_MAX_BINARY_FRAGMENT,
origlen, sent,
channel->mBufferedData.Length()));
@@ -2078,17 +2051,17 @@ DataChannelConnection::SendBinary(DataCh
// This will fail if the message is too large
return SendMsgInternal(channel, data, len, DATA_CHANNEL_PPID_BINARY_LAST);
}
int32_t
DataChannelConnection::SendBlob(uint16_t stream, nsIInputStream *aBlob)
{
- DataChannel *channel = mStreamsOut[stream];
+ DataChannel *channel = mStreams[stream];
NS_ENSURE_TRUE(channel, 0);
// Spawn a thread to send the data
LOG(("Sending blob to stream %u", stream));
// XXX to do this safely, we must enqueue these atomically onto the
// output socket. We need a sender thread(s?) to enque data into the
// socket and to avoid main-thread IO that might block. Even on a
@@ -2123,25 +2096,25 @@ DataChannelConnection::SendBlob(uint16_t
int32_t
DataChannelConnection::SendMsgCommon(uint16_t stream, const nsACString &aMsg,
bool isBinary)
{
ASSERT_WEBRTC(NS_IsMainThread());
// We really could allow this from other threads, so long as we deal with
// asynchronosity issues with channels closing, in particular access to
- // mStreamsOut, and issues with the association closing (access to mSocket).
+ // mStreams, and issues with the association closing (access to mSocket).
const char *data = aMsg.BeginReading();
uint32_t len = aMsg.Length();
DataChannel *channel;
LOG(("Sending %sto stream %u: %u bytes", isBinary ? "binary " : "", stream, len));
// XXX if we want more efficiency, translate flags once at open time
- channel = mStreamsOut[stream];
+ channel = mStreams[stream];
NS_ENSURE_TRUE(channel, 0);
if (isBinary)
return SendBinary(channel, data, len);
return SendMsgInternal(channel, data, len, DATA_CHANNEL_PPID_DOMSTRING);
}
void
@@ -2155,68 +2128,68 @@ DataChannelConnection::Close(DataChannel
// Called from someone who holds a ref via ::Close(), or from ~DataChannel
void
DataChannelConnection::CloseInt(DataChannel *aChannel)
{
MOZ_ASSERT(aChannel);
nsRefPtr<DataChannel> channel(aChannel); // make sure it doesn't go away on us
mLock.AssertCurrentThreadOwns();
- LOG(("Connection %p/Channel %p: Closing stream %d",
- aChannel->mConnection.get(), aChannel, aChannel->mStreamOut));
+ LOG(("Connection %p/Channel %p: Closing stream %u",
+ channel->mConnection.get(), channel.get(), channel->mStream));
// re-test since it may have closed before the lock was grabbed
if (aChannel->mState == CLOSED || aChannel->mState == CLOSING) {
- LOG(("Channel already closing/closed (%d)", aChannel->mState));
+ LOG(("Channel already closing/closed (%u)", aChannel->mState));
return;
}
aChannel->mBufferedData.Clear();
- if (aChannel->mStreamOut != INVALID_STREAM) {
- ResetOutgoingStream(aChannel->mStreamOut);
+ if (channel->mStream != INVALID_STREAM) {
+ ResetOutgoingStream(channel->mStream);
if (mState == CLOSED) { // called from CloseAll()
// Let resets accumulate then send all at once in CloseAll()
// we're not going to hang around waiting
- mStreamsOut[aChannel->mStreamOut] = nullptr;
+ mStreams[channel->mStream] = nullptr;
} else {
SendOutgoingStreamReset();
}
}
aChannel->mState = CLOSING;
if (mState == CLOSED) {
// we're not going to hang around waiting
- if (channel->mStreamOut != INVALID_STREAM) {
- mStreamsIn[channel->mStreamIn] = nullptr;
- }
channel->Destroy();
}
// At this point when we leave here, the object is a zombie held alive only by the DOM object
}
void DataChannelConnection::CloseAll()
{
LOG(("Closing all channels"));
// Don't need to lock here
// Make sure no more channels will be opened
- mState = CLOSED;
+ {
+ MutexAutoLock lock(mLock);
+ mState = CLOSED;
+ }
// Close current channels
// If there are runnables, they hold a strong ref and keep the channel
// and/or connection alive (even if in a CLOSED state)
bool closed_some = false;
- for (uint32_t i = 0; i < mStreamsOut.Length(); ++i) {
- if (mStreamsOut[i]) {
- mStreamsOut[i]->Close();
+ for (uint32_t i = 0; i < mStreams.Length(); ++i) {
+ if (mStreams[i]) {
+ mStreams[i]->Close();
closed_some = true;
}
}
// Clean up any pending opens for channels
nsRefPtr<DataChannel> channel;
while (nullptr != (channel = dont_AddRef(static_cast<DataChannel *>(mPending.PopFront())))) {
- LOG(("closing pending channel %p, stream %d", channel.get(), channel->mStreamOut));
+ LOG(("closing pending channel %p, stream %u", channel.get(), channel->mStream));
channel->Close(); // also releases the ref on each iteration
closed_some = true;
}
// It's more efficient to let the Resets queue in shutdown and then
// SendOutgoingStreamReset() here.
if (closed_some) {
MutexAutoLock lock(mLock);
SendOutgoingStreamReset();
@@ -2239,23 +2212,20 @@ DataChannel::Close()
}
// Used when disconnecting from the DataChannelConnection
void
DataChannel::Destroy()
{
ENSURE_DATACONNECTION;
- LOG(("Destroying Data channel %d/%d", mStreamOut, mStreamIn));
- MOZ_ASSERT_IF(mStreamOut != INVALID_STREAM,
- !mConnection->FindChannelByStreamOut(mStreamOut));
- MOZ_ASSERT_IF(mStreamIn != INVALID_STREAM,
- !mConnection->FindChannelByStreamIn(mStreamIn));
- mStreamIn = INVALID_STREAM;
- mStreamOut = INVALID_STREAM;
+ LOG(("Destroying Data channel %u", mStream));
+ MOZ_ASSERT_IF(mStream != INVALID_STREAM,
+ !mConnection->FindChannelByStream(mStream));
+ mStream = INVALID_STREAM;
mState = CLOSED;
mConnection = nullptr;
}
void
DataChannel::SetListener(DataChannelListener *aListener, nsISupports *aContext)
{
MutexAutoLock mLock(mListenerLock);
--- a/netwerk/sctp/datachannel/DataChannel.h
+++ b/netwerk/sctp/datachannel/DataChannel.h
@@ -23,16 +23,17 @@
#include "nsIInputStream.h"
#include "nsITimer.h"
#include "mozilla/Mutex.h"
#include "DataChannelProtocol.h"
#ifdef SCTP_DTLS_SUPPORTED
#include "mtransport/sigslot.h"
#include "mtransport/transportflow.h"
#include "mtransport/transportlayer.h"
+#include "mtransport/transportlayerdtls.h"
#include "mtransport/transportlayerprsock.h"
#endif
#ifndef DATACHANNEL_LOG
#define DATACHANNEL_LOG(args)
#endif
#ifndef EALREADY
@@ -46,28 +47,55 @@ extern "C" {
namespace mozilla {
class DTLSConnection;
class DataChannelConnection;
class DataChannel;
class DataChannelOnMessageAvailable;
+// For queuing outgoing messages
class BufferedMsg
{
public:
BufferedMsg(struct sctp_sendv_spa &spa,const char *data,
uint32_t length);
~BufferedMsg();
struct sctp_sendv_spa *mSpa;
const char *mData;
uint32_t mLength;
};
+// for queuing incoming data messages before the Open or
+// external negotiation is indicated to us
+class QueuedDataMessage
+{
+public:
+ QueuedDataMessage(uint16_t stream, uint32_t ppid,
+ const void *data, size_t length)
+ : mStream(stream)
+ , mPpid(ppid)
+ , mLength(length)
+ {
+ mData = static_cast<char *>(moz_xmalloc(length)); // infallible
+ memcpy(mData, data, length);
+ }
+
+ ~QueuedDataMessage()
+ {
+ moz_free(mData);
+ }
+
+ uint16_t mStream;
+ uint32_t mPpid;
+ size_t mLength;
+ char *mData;
+};
+
// Implemented by consumers of a Channel to receive messages.
// Can't nest it in DataChannelConnection because C++ doesn't allow forward
// refs to embedded classes
class DataChannelListener {
public:
virtual ~DataChannelListener() {}
// Called when a DOMString message is received.
@@ -112,38 +140,46 @@ public:
};
DataChannelConnection(DataConnectionListener *listener);
virtual ~DataChannelConnection();
bool Init(unsigned short aPort, uint16_t aNumStreams, bool aUsingDtls);
void Destroy(); // So we can spawn refs tied to runnables in shutdown
+#ifdef ALLOW_DIRECT_SCTP_LISTEN_CONNECT
// These block; they require something to decide on listener/connector
// (though you can do simultaneous Connect()). Do not call these from
// the main thread!
bool Listen(unsigned short port);
bool Connect(const char *addr, unsigned short port);
+#endif
#ifdef SCTP_DTLS_SUPPORTED
// Connect using a TransportFlow (DTLS) channel
- bool ConnectDTLS(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport);
+ void SetEvenOdd();
+ bool ConnectViaTransportFlow(TransportFlow *aFlow, uint16_t localport, uint16_t remoteport);
+ void CompleteConnect(TransportFlow *flow, TransportLayer::State state);
+ void SetSignals();
#endif
typedef enum {
RELIABLE=0,
PARTIAL_RELIABLE_REXMIT = 1,
PARTIAL_RELIABLE_TIMED = 2
} Type;
already_AddRefed<DataChannel> Open(const nsACString& label,
+ const nsACString& protocol,
Type type, bool inOrder,
uint32_t prValue,
DataChannelListener *aListener,
- nsISupports *aContext);
+ nsISupports *aContext,
+ bool aExternalNegotiated,
+ uint16_t aStream);
void Close(DataChannel *aChannel);
// CloseInt() must be called with mLock held
void CloseInt(DataChannel *aChannel);
void CloseAll();
int32_t SendMsg(uint16_t stream, const nsACString &aMsg)
{
@@ -162,17 +198,17 @@ public:
// Find out state
enum {
CONNECTING = 0U,
OPEN = 1U,
CLOSING = 2U,
CLOSED = 3U
};
- uint16_t GetReadyState() { return mState; }
+ uint16_t GetReadyState() { MutexAutoLock lock(mLock); return mState; }
friend class DataChannel;
Mutex mLock;
protected:
friend class DataChannelOnMessageAvailable;
// Avoid cycles with PeerConnectionImpl
// Use from main thread only as WeakPtr is not threadsafe
@@ -182,45 +218,41 @@ private:
friend class DataChannelConnectRunnable;
#ifdef SCTP_DTLS_SUPPORTED
static void DTLSConnectThread(void *data);
int SendPacket(const unsigned char* data, size_t len, bool release);
void SctpDtlsInput(TransportFlow *flow, const unsigned char *data, size_t len);
static int SctpDtlsOutput(void *addr, void *buffer, size_t length, uint8_t tos, uint8_t set_df);
#endif
- DataChannel* FindChannelByStreamIn(uint16_t streamIn);
- DataChannel* FindChannelByStreamOut(uint16_t streamOut);
- uint16_t FindFreeStreamOut();
- bool RequestMoreStreamsOut(int32_t aNeeded = 16);
+ DataChannel* FindChannelByStream(uint16_t stream);
+ uint16_t FindFreeStream();
+ bool RequestMoreStreams(int32_t aNeeded = 16);
int32_t SendControlMessage(void *msg, uint32_t len, uint16_t streamOut);
- int32_t SendOpenRequestMessage(const nsACString& label,uint16_t streamOut,
+ int32_t SendOpenRequestMessage(const nsACString& label, const nsACString& protocol,
+ uint16_t streamOut,
bool unordered, uint16_t prPolicy, uint32_t prValue);
- int32_t SendOpenResponseMessage(uint16_t streamOut, uint16_t streamIn);
- int32_t SendOpenAckMessage(uint16_t streamOut);
int32_t SendMsgInternal(DataChannel *channel, const char *data,
uint32_t length, uint32_t ppid);
int32_t SendBinary(DataChannel *channel, const char *data,
uint32_t len);
int32_t SendMsgCommon(uint16_t stream, const nsACString &aMsg, bool isBinary);
+ void DeliverQueuedData(uint16_t stream);
+
already_AddRefed<DataChannel> OpenFinish(already_AddRefed<DataChannel> channel);
void StartDefer();
bool SendDeferredMessages();
+ void ProcessQueuedOpens();
void SendOutgoingStreamReset();
void ResetOutgoingStream(uint16_t streamOut);
void HandleOpenRequestMessage(const struct rtcweb_datachannel_open_request *req,
size_t length,
uint16_t streamIn);
- void OpenResponseFinish(already_AddRefed<DataChannel> channel);
- void HandleOpenResponseMessage(const struct rtcweb_datachannel_open_response *rsp,
- size_t length, uint16_t streamIn);
- void HandleOpenAckMessage(const struct rtcweb_datachannel_ack *ack,
- size_t length, uint16_t streamIn);
void HandleUnknownMessage(uint32_t ppid, size_t length, uint16_t streamIn);
void HandleDataMessage(uint32_t ppid, const void *buffer, size_t length, uint16_t streamIn);
void HandleMessage(const void *buffer, size_t length, uint32_t ppid, uint16_t streamIn);
void HandleAssociationChangeEvent(const struct sctp_assoc_change *sac);
void HandlePeerAddressChangeEvent(const struct sctp_paddr_change *spc);
void HandleRemoteErrorEvent(const struct sctp_remote_error *sre);
void HandleShutdownEvent(const struct sctp_shutdown_event *sse);
void HandleAdaptationIndication(const struct sctp_adaptation_event *sai);
@@ -238,35 +270,38 @@ private:
return on;
}
#endif
// Exists solely for proxying release of the TransportFlow to the STS thread
static void ReleaseTransportFlow(nsRefPtr<TransportFlow> aFlow) {}
// Data:
- // NOTE: while these arrays will auto-expand, increases in the number of
+ // NOTE: while this array will auto-expand, increases in the number of
// channels available from the stack must be negotiated!
- nsAutoTArray<nsRefPtr<DataChannel>,16> mStreamsOut;
- nsAutoTArray<nsRefPtr<DataChannel>,16> mStreamsIn;
+ bool mAllocateEven;
+ nsAutoTArray<nsRefPtr<DataChannel>,16> mStreams;
nsDeque mPending; // Holds already_AddRefed<DataChannel>s -- careful!
+ // holds data that's come in before a channel is open
+ nsTArray<nsAutoPtr<QueuedDataMessage> > mQueuedData;
// Streams pending reset
nsAutoTArray<uint16_t,4> mStreamsResetting;
- struct socket *mMasterSocket; // accessed from connect thread
- struct socket *mSocket; // cloned from mMasterSocket on successful Connect on connect thread
- uint16_t mState; // modified on connect thread (to OPEN)
+ struct socket *mMasterSocket; // accessed from STS thread
+ struct socket *mSocket; // cloned from mMasterSocket on successful Connect on STS thread
+ uint16_t mState; // Protected with mLock
#ifdef SCTP_DTLS_SUPPORTED
nsRefPtr<TransportFlow> mTransportFlow;
nsCOMPtr<nsIEventTarget> mSTS;
#endif
uint16_t mLocalPort; // Accessed from connect thread
uint16_t mRemotePort;
+ bool mUsingDtls;
// Timer to control when we try to resend blocked messages
nsCOMPtr<nsITimer> mDeferredTimer;
uint32_t mDeferTimeout; // in ms
bool mTimerRunning;
};
#define ENSURE_DATACONNECTION \
@@ -281,32 +316,33 @@ public:
CONNECTING = 0U,
OPEN = 1U,
CLOSING = 2U,
CLOSED = 3U,
WAITING_TO_OPEN = 4U
};
DataChannel(DataChannelConnection *connection,
- uint16_t streamOut, uint16_t streamIn,
+ uint16_t stream,
uint16_t state,
const nsACString& label,
+ const nsACString& protocol,
uint16_t policy, uint32_t value,
uint32_t flags,
DataChannelListener *aListener,
nsISupports *aContext)
: mListenerLock("netwerk::sctp::DataChannel")
, mListener(aListener)
, mContext(aContext)
, mConnection(connection)
, mLabel(label)
+ , mProtocol(protocol)
, mState(state)
, mReady(false)
- , mStreamOut(streamOut)
- , mStreamIn(streamIn)
+ , mStream(stream)
, mPrPolicy(policy)
, mPrValue(value)
, mFlags(0)
{
NS_ASSERTION(mConnection,"NULL connection");
}
~DataChannel();
@@ -321,62 +357,64 @@ public:
// Set the listener (especially for channels created from the other side)
void SetListener(DataChannelListener *aListener, nsISupports *aContext);
// Send a string
bool SendMsg(const nsACString &aMsg)
{
ENSURE_DATACONNECTION_RET(false);
- if (mStreamOut != INVALID_STREAM)
- return (mConnection->SendMsg(mStreamOut, aMsg) > 0);
+ if (mStream != INVALID_STREAM)
+ return (mConnection->SendMsg(mStream, aMsg) > 0);
else
return false;
}
// Send a binary message (TypedArray)
bool SendBinaryMsg(const nsACString &aMsg)
{
ENSURE_DATACONNECTION_RET(false);
- i