This commit is contained in:
Rob Campbell 2011-04-21 13:18:12 -03:00
Родитель 19eba7bd60 5f410e5e8c
Коммит 9063436548
109 изменённых файлов: 2908 добавлений и 760 удалений

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

@ -26,6 +26,28 @@
<key>CFBundleTypeRole</key> <key>CFBundleTypeRole</key>
<string>Viewer</string> <string>Viewer</string>
</dict> </dict>
<dict>
<key>CFBundleTypeExtensions</key>
<array>
<string>svg</string>
</array>
<key>CFBundleTypeIconFile</key>
<string>document.icns</string>
<key>CFBundleTypeMIMETypes</key>
<array>
<string>image/svg+xml</string>
</array>
<key>CFBundleTypeName</key>
<string>SVG document</string>
<key>CFBundleTypeOSTypes</key>
<array>
<string>TEXT</string>
</array>
<key>CFBundleTypeRole</key>
<string>Viewer</string>
<key>NSDocumentClass</key>
<string>BrowserDocument</string>
</dict>
<dict> <dict>
<key>CFBundleTypeExtensions</key> <key>CFBundleTypeExtensions</key>
<array> <array>

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

@ -946,6 +946,7 @@ pref("toolbar.customization.usesheet", false);
pref("dom.ipc.plugins.enabled.i386", false); pref("dom.ipc.plugins.enabled.i386", false);
pref("dom.ipc.plugins.enabled.i386.flash player.plugin", true); pref("dom.ipc.plugins.enabled.i386.flash player.plugin", true);
pref("dom.ipc.plugins.enabled.i386.javaplugin2_npapi.plugin", true); pref("dom.ipc.plugins.enabled.i386.javaplugin2_npapi.plugin", true);
pref("dom.ipc.plugins.enabled.i386.javaappletplugin.plugin", true);
// x86_64 ipc preferences // x86_64 ipc preferences
pref("dom.ipc.plugins.enabled.x86_64", true); pref("dom.ipc.plugins.enabled.x86_64", true);
#else #else

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

@ -134,13 +134,16 @@
margin-top: 10px; margin-top: 10px;
} }
#channelSelectorStart {
-moz-margin-start: 0;
}
#channelMenulist { #channelMenulist {
margin: 0; margin: 0;
} }
.channel-description { .channel-description {
margin: 10px 0; margin: 10px 0;
text-align: center;
} }
#detailsBox, #detailsBox,

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

@ -127,8 +127,8 @@
</vbox> </vbox>
<vbox id="channelSelector"> <vbox id="channelSelector">
<hbox pack="center" align="center"> <hbox pack="start" align="center">
<label>&channel.selector.start;</label> <label id="channelSelectorStart">&channel.selector.start;</label>
<menulist id="channelMenulist" onselect="gChannelSelector.selectChannel(this.selectedItem);"> <menulist id="channelMenulist" onselect="gChannelSelector.selectChannel(this.selectedItem);">
<menupopup> <menupopup>
<menuitem id="releaseMenuitem" label="Release" value="release"/> <menuitem id="releaseMenuitem" label="Release" value="release"/>
@ -146,9 +146,14 @@
<description id="auroraDescription" class="channel-description">&channel.aurora.description;</description> <description id="auroraDescription" class="channel-description">&channel.aurora.description;</description>
</deck> </deck>
<hbox id="channelSelectorButtons" pack="center"> <hbox id="channelSelectorButtons" pack="end">
#ifdef XP_UNIX
<button oncommand="gChannelSelector.cancel();" label="&channel.selector.cancelButton;"/>
<button oncommand="gChannelSelector.apply();" label="&channel.selector.applyButton;"/>
#else
<button oncommand="gChannelSelector.apply();" label="&channel.selector.applyButton;"/> <button oncommand="gChannelSelector.apply();" label="&channel.selector.applyButton;"/>
<button oncommand="gChannelSelector.cancel();" label="&channel.selector.cancelButton;"/> <button oncommand="gChannelSelector.cancel();" label="&channel.selector.cancelButton;"/>
#endif
</hbox> </hbox>
</vbox> </vbox>
</deck> </deck>

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

@ -2860,17 +2860,22 @@ SessionStoreService.prototype = {
// Attach data that will be restored on "load" event, after tab is restored. // Attach data that will be restored on "load" event, after tab is restored.
if (activeIndex > -1) { if (activeIndex > -1) {
let curSHEntry = browser.webNavigation.sessionHistory.
getEntryAtIndex(activeIndex, false).
QueryInterface(Ci.nsISHEntry);
// restore those aspects of the currently active documents which are not // restore those aspects of the currently active documents which are not
// preserved in the plain history entries (mainly scroll state and text data) // preserved in the plain history entries (mainly scroll state and text data)
browser.__SS_restore_data = tabData.entries[activeIndex] || {}; browser.__SS_restore_data = tabData.entries[activeIndex] || {};
browser.__SS_restore_pageStyle = tabData.pageStyle || ""; browser.__SS_restore_pageStyle = tabData.pageStyle || "";
browser.__SS_restore_tab = aTab; browser.__SS_restore_tab = aTab;
browser.__SS_restore_docIdentifier = curSHEntry.docIdentifier;
didStartLoad = true; didStartLoad = true;
try { try {
// In order to work around certain issues in session history, we need to // In order to work around certain issues in session history, we need to
// force session history to update its internal index and call reload // force session history to update its internal index and call reload
// instead of gotoIndex. c.f. bug 597315 // instead of gotoIndex. See bug 597315.
browser.webNavigation.sessionHistory.getEntryAtIndex(activeIndex, true); browser.webNavigation.sessionHistory.getEntryAtIndex(activeIndex, true);
browser.webNavigation.sessionHistory. browser.webNavigation.sessionHistory.
QueryInterface(Ci.nsISHistory_2_0_BRANCH).reloadCurrentEntry(); QueryInterface(Ci.nsISHistory_2_0_BRANCH).reloadCurrentEntry();
@ -3163,12 +3168,19 @@ SessionStoreService.prototype = {
aBrowser.markupDocumentViewer.authorStyleDisabled = selectedPageStyle == "_nostyle"; aBrowser.markupDocumentViewer.authorStyleDisabled = selectedPageStyle == "_nostyle";
} }
if (aBrowser.__SS_restore_docIdentifier) {
let sh = aBrowser.webNavigation.sessionHistory;
sh.getEntryAtIndex(sh.index, false).QueryInterface(Ci.nsISHEntry).
docIdentifier = aBrowser.__SS_restore_docIdentifier;
}
// notify the tabbrowser that this document has been completely restored // notify the tabbrowser that this document has been completely restored
this._sendTabRestoredNotification(aBrowser.__SS_restore_tab); this._sendTabRestoredNotification(aBrowser.__SS_restore_tab);
delete aBrowser.__SS_restore_data; delete aBrowser.__SS_restore_data;
delete aBrowser.__SS_restore_pageStyle; delete aBrowser.__SS_restore_pageStyle;
delete aBrowser.__SS_restore_tab; delete aBrowser.__SS_restore_tab;
delete aBrowser.__SS_restore_docIdentifier;
}, },
/** /**

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

@ -44,11 +44,15 @@ function checkState(tab) {
let popStateCount = 0; let popStateCount = 0;
let handler = function(aEvent) { tab.linkedBrowser.addEventListener('popstate', function(aEvent) {
let contentWindow = tab.linkedBrowser.contentWindow; let contentWindow = tab.linkedBrowser.contentWindow;
if (popStateCount == 0) { if (popStateCount == 0) {
popStateCount++; popStateCount++;
//ok(aEvent.state, "Event should have a state property.");
is(tab.linkedBrowser.contentWindow.testState, 'foo',
'testState after going back');
ok(aEvent.state, "Event should have a state property.");
is(JSON.stringify(tab.linkedBrowser.contentWindow.history.state), JSON.stringify({obj1:1}), is(JSON.stringify(tab.linkedBrowser.contentWindow.history.state), JSON.stringify({obj1:1}),
"first popstate object."); "first popstate object.");
@ -78,15 +82,16 @@ function checkState(tab) {
// Clean up after ourselves and finish the test. // Clean up after ourselves and finish the test.
tab.linkedBrowser.removeEventListener("popstate", arguments.callee, true); tab.linkedBrowser.removeEventListener("popstate", arguments.callee, true);
tab.linkedBrowser.removeEventListener("load", arguments.callee, true);
gBrowser.removeTab(tab); gBrowser.removeTab(tab);
finish(); finish();
} }
}; }, true);
tab.linkedBrowser.addEventListener("load", handler, true); // Set some state in the page's window. When we go back(), the page should
tab.linkedBrowser.addEventListener("popstate", handler, true); // be retrieved from bfcache, and this state should still be there.
tab.linkedBrowser.contentWindow.testState = 'foo';
// Now go back. This should trigger the popstate event handler above.
tab.linkedBrowser.contentWindow.history.back(); tab.linkedBrowser.contentWindow.history.back();
} }
@ -119,20 +124,24 @@ function test() {
let contentWindow = tab.linkedBrowser.contentWindow; let contentWindow = tab.linkedBrowser.contentWindow;
let history = contentWindow.history; let history = contentWindow.history;
history.pushState({obj1:1}, "title-obj1"); history.pushState({obj1:1}, "title-obj1");
history.pushState({obj2:2}, "title-obj2", "?foo"); history.pushState({obj2:2}, "title-obj2", "page2");
history.replaceState({obj3:3}, "title-obj3"); history.replaceState({obj3:3}, "title-obj3");
let state = ss.getTabState(tab); let state = ss.getTabState(tab);
gBrowser.removeTab(tab);
// In order to make sure that setWindowState actually modifies the // Restore the state into a new tab. Things don't work well when we
// window's state, we modify the state here. checkState will fail if // restore into the old tab, but that's not a real use case anyway.
// this change isn't overwritten by setWindowState. let tab2 = gBrowser.addTab("about:blank");
history.replaceState({should_be_overwritten:true}, "title-overwritten"); ss.setTabState(tab2, state, true);
// Restore the state and make sure it looks right, after giving the event // Run checkState() once the tab finishes loading its restored state.
// loop a chance to flush. tab2.linkedBrowser.addEventListener("load", function() {
ss.setTabState(tab, state, true); tab2.linkedBrowser.removeEventListener("load", arguments.callee, true);
executeSoon(function() { checkState(tab); }); SimpleTest.executeSoon(function() {
checkState(tab2);
});
}, true);
}, true); }, true);
}, true); }, true);

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

@ -61,7 +61,6 @@ namespace std {
} }
namespace std __attribute__((visibility("default"))) { namespace std __attribute__((visibility("default"))) {
#if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 5) #if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 5)
/* Hack to avoid GLIBCXX_3.4.14 symbol versions */ /* Hack to avoid GLIBCXX_3.4.14 symbol versions */
struct _List_node_base struct _List_node_base
@ -73,35 +72,61 @@ namespace std __attribute__((visibility("default"))) {
void transfer(_List_node_base * const __first, void transfer(_List_node_base * const __first,
_List_node_base * const __last) throw(); _List_node_base * const __last) throw();
/* Hack to avoid GLIBCXX_3.4.15 symbol versions */
#if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)
static void swap(_List_node_base& __x, _List_node_base& __y) throw ();
};
namespace __detail {
struct _List_node_base
{
#endif
void _M_hook(_List_node_base * const __position) throw (); void _M_hook(_List_node_base * const __position) throw ();
void _M_unhook() throw (); void _M_unhook() throw ();
void _M_transfer(_List_node_base * const __first, void _M_transfer(_List_node_base * const __first,
_List_node_base * const __last) throw(); _List_node_base * const __last) throw();
#if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)
static void swap(_List_node_base& __x, _List_node_base& __y) throw ();
#endif
}; };
/* The functions actually have the same implementation */ /* The functions actually have the same implementation */
void void
_List_node_base::_M_hook(_List_node_base * const __position) throw () _List_node_base::_M_hook(_List_node_base * const __position) throw ()
{ {
hook(__position); ((std::_List_node_base *)this)->hook((std::_List_node_base * const) __position);
} }
void void
_List_node_base::_M_unhook() throw () _List_node_base::_M_unhook() throw ()
{ {
unhook(); ((std::_List_node_base *)this)->unhook();
} }
void void
_List_node_base::_M_transfer(_List_node_base * const __first, _List_node_base::_M_transfer(_List_node_base * const __first,
_List_node_base * const __last) throw () _List_node_base * const __last) throw ()
{ {
transfer(__first, __last); ((std::_List_node_base *)this)->transfer((std::_List_node_base * const)__first,
(std::_List_node_base * const)__last);
} }
#if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 6)
void
_List_node_base::swap(_List_node_base& __x, _List_node_base& __y) throw ()
{
std::_List_node_base::swap(*((std::_List_node_base *) &__x),
*((std::_List_node_base *) &__y));
}
}
#endif #endif
#endif /* (__GNUC__ == 4) && (__GNUC_MINOR__ >= 5) */
#if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 4) #if (__GNUC__ == 4) && (__GNUC_MINOR__ >= 4)
/* Hack to avoid GLIBCXX_3.4.11 symbol versions /* Hack to avoid GLIBCXX_3.4.11 symbol versions
An inline definition of ctype<char>::_M_widen_init() used to be in An inline definition of ctype<char>::_M_widen_init() used to be in

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

@ -707,6 +707,8 @@ ANDROID_PACKAGE_NAME = @ANDROID_PACKAGE_NAME@
JS_SHARED_LIBRARY = @JS_SHARED_LIBRARY@ JS_SHARED_LIBRARY = @JS_SHARED_LIBRARY@
MOZ_INSTRUMENT_EVENT_LOOP = @MOZ_INSTRUMENT_EVENT_LOOP@
# We only want to do the pymake sanity on Windows, other os's can cope # We only want to do the pymake sanity on Windows, other os's can cope
ifeq ($(HOST_OS_ARCH),WINNT) ifeq ($(HOST_OS_ARCH),WINNT)
# Ensure invariants between GNU Make and pymake # Ensure invariants between GNU Make and pymake

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

@ -82,11 +82,7 @@ else
ELOG := ELOG :=
endif endif
ifeq ($(OS_ARCH),WINNT)
_VPATH_SRCS = $(abspath $<) _VPATH_SRCS = $(abspath $<)
else
_VPATH_SRCS = $<
endif
# Add $(DIST)/lib to VPATH so that -lfoo dependencies are followed # Add $(DIST)/lib to VPATH so that -lfoo dependencies are followed
VPATH += $(DIST)/lib VPATH += $(DIST)/lib

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

@ -2456,7 +2456,11 @@ ia64*-hpux*)
;; ;;
*-openbsd*) *-openbsd*)
DLL_SUFFIX='.so.$(if $(SO_VERSION),$(SO_VERSION),1.0)' if test "$SO_VERSION"; then
DLL_SUFFIX=".so.$SO_VERSION"
else
DLL_SUFFIX=".so.1.0"
fi
MOZ_FIX_LINK_PATHS='-Wl,-rpath-link,$(LIBXUL_DIST)/bin -Wl,-rpath-link,$(prefix)/lib -Wl,-rpath-link,$(if $(X11BASE),$(X11BASE),/usr/X11R6)/lib' MOZ_FIX_LINK_PATHS='-Wl,-rpath-link,$(LIBXUL_DIST)/bin -Wl,-rpath-link,$(prefix)/lib -Wl,-rpath-link,$(if $(X11BASE),$(X11BASE),/usr/X11R6)/lib'
DSO_CFLAGS='' DSO_CFLAGS=''
DSO_PIC_CFLAGS='-fPIC' DSO_PIC_CFLAGS='-fPIC'
@ -5019,6 +5023,7 @@ cairo-windows)
MOZ_WIDGET_TOOLKIT=windows MOZ_WIDGET_TOOLKIT=windows
MOZ_WEBGL=1 MOZ_WEBGL=1
MOZ_PDF_PRINTING=1 MOZ_PDF_PRINTING=1
MOZ_INSTRUMENT_EVENT_LOOP=1
;; ;;
cairo-gtk2|cairo-gtk2-x11) cairo-gtk2|cairo-gtk2-x11)
@ -5036,6 +5041,7 @@ cairo-gtk2|cairo-gtk2-x11)
TK_LIBS='$(MOZ_GTK2_LIBS)' TK_LIBS='$(MOZ_GTK2_LIBS)'
AC_DEFINE(MOZ_WIDGET_GTK2) AC_DEFINE(MOZ_WIDGET_GTK2)
MOZ_PDF_PRINTING=1 MOZ_PDF_PRINTING=1
MOZ_INSTRUMENT_EVENT_LOOP=1
;; ;;
cairo-gtk2-dfb) cairo-gtk2-dfb)
@ -5095,6 +5101,7 @@ cairo-cocoa)
LIBXUL_LIBS='$(XPCOM_FROZEN_LDOPTS) $(LIBXUL_DIST)/bin/XUL' LIBXUL_LIBS='$(XPCOM_FROZEN_LDOPTS) $(LIBXUL_DIST)/bin/XUL'
MOZ_FS_LAYOUT=bundle MOZ_FS_LAYOUT=bundle
MOZ_WEBGL=1 MOZ_WEBGL=1
MOZ_INSTRUMENT_EVENT_LOOP=1
;; ;;
cairo-android) cairo-android)
@ -5118,6 +5125,10 @@ if test "$MOZ_ENABLE_XREMOTE"; then
AC_DEFINE(MOZ_ENABLE_XREMOTE) AC_DEFINE(MOZ_ENABLE_XREMOTE)
fi fi
if test "$MOZ_INSTRUMENT_EVENT_LOOP"; then
AC_DEFINE(MOZ_INSTRUMENT_EVENT_LOOP)
fi
if test "$COMPILE_ENVIRONMENT"; then if test "$COMPILE_ENVIRONMENT"; then
if test "$MOZ_ENABLE_GTK2"; then if test "$MOZ_ENABLE_GTK2"; then
if test "$MOZ_X11"; then if test "$MOZ_X11"; then
@ -8988,6 +8999,7 @@ AC_SUBST(VPX_AS_CONVERSION)
AC_SUBST(VPX_ASM_SUFFIX) AC_SUBST(VPX_ASM_SUFFIX)
AC_SUBST(VPX_X86_ASM) AC_SUBST(VPX_X86_ASM)
AC_SUBST(VPX_ARM_ASM) AC_SUBST(VPX_ARM_ASM)
AC_SUBST(MOZ_INSTRUMENT_EVENT_LOOP)
AC_SUBST(LIBJPEG_TURBO_AS) AC_SUBST(LIBJPEG_TURBO_AS)
AC_SUBST(LIBJPEG_TURBO_ASFLAGS) AC_SUBST(LIBJPEG_TURBO_ASFLAGS)
AC_SUBST(LIBJPEG_TURBO_X86_ASM) AC_SUBST(LIBJPEG_TURBO_X86_ASM)

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

@ -1414,6 +1414,14 @@ public:
*/ */
static PRBool URIIsLocalFile(nsIURI *aURI); static PRBool URIIsLocalFile(nsIURI *aURI);
/**
* Given a URI, return set beforeHash to the part before the '#', and
* afterHash to the remainder of the URI, including the '#'.
*/
static nsresult SplitURIAtHash(nsIURI *aURI,
nsACString &aBeforeHash,
nsACString &aAfterHash);
/** /**
* Get the application manifest URI for this document. The manifest URI * Get the application manifest URI for this document. The manifest URI
* is specified in the manifest= attribute of the root element of the * is specified in the manifest= attribute of the root element of the

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

@ -5012,6 +5012,32 @@ nsContentUtils::URIIsLocalFile(nsIURI *aURI)
isFile; isFile;
} }
nsresult
nsContentUtils::SplitURIAtHash(nsIURI *aURI,
nsACString &aBeforeHash,
nsACString &aAfterHash)
{
// See bug 225910 for why we can't do this using nsIURL.
aBeforeHash.Truncate();
aAfterHash.Truncate();
NS_ENSURE_ARG_POINTER(aURI);
nsCAutoString spec;
nsresult rv = aURI->GetSpec(spec);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 index = spec.FindChar('#');
if (index == -1) {
index = spec.Length();
}
aBeforeHash.Assign(Substring(spec, 0, index));
aAfterHash.Assign(Substring(spec, index));
return NS_OK;
}
/* static */ /* static */
nsIScriptContext* nsIScriptContext*
nsContentUtils::GetContextForEventHandlers(nsINode* aNode, nsContentUtils::GetContextForEventHandlers(nsINode* aNode,

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

@ -42,7 +42,7 @@ function testCancelInPhase4() {
testCancelBeforePhase4(); testCancelBeforePhase4();
}, false); }, false);
xhr2.open("GET", url, false); // note : synch-request xhr2.open("GET", url);
xhr2.setRequestHeader("X-Request", "1", false); xhr2.setRequestHeader("X-Request", "1", false);
try { xhr2.send(); } try { xhr2.send(); }
@ -87,7 +87,7 @@ function testCancelBeforePhase4() {
SimpleTest.finish(); SimpleTest.finish();
}, false); }, false);
xhr2.open("GET", url, false); // note : synch-request xhr2.open("GET", url);
xhr2.setRequestHeader("X-Request", "1", false); xhr2.setRequestHeader("X-Request", "1", false);
try { xhr2.send(); } try { xhr2.send(); }

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

@ -85,6 +85,7 @@ CPPSRCS = \
nsDOMScrollAreaEvent.cpp \ nsDOMScrollAreaEvent.cpp \
nsDOMTransitionEvent.cpp \ nsDOMTransitionEvent.cpp \
nsDOMPopStateEvent.cpp \ nsDOMPopStateEvent.cpp \
nsDOMHashChangeEvent.cpp \
nsDOMCloseEvent.cpp \ nsDOMCloseEvent.cpp \
$(NULL) $(NULL)

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

@ -0,0 +1,89 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsDOMHashChangeEvent.h"
#include "nsContentUtils.h"
NS_IMPL_ADDREF_INHERITED(nsDOMHashChangeEvent, nsDOMEvent)
NS_IMPL_RELEASE_INHERITED(nsDOMHashChangeEvent, nsDOMEvent)
DOMCI_DATA(HashChangeEvent, nsDOMHashChangeEvent)
NS_INTERFACE_MAP_BEGIN(nsDOMHashChangeEvent)
NS_INTERFACE_MAP_ENTRY(nsIDOMHashChangeEvent)
NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(HashChangeEvent)
NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
nsDOMHashChangeEvent::~nsDOMHashChangeEvent()
{
}
NS_IMETHODIMP
nsDOMHashChangeEvent::GetOldURL(nsAString &aURL)
{
aURL.Assign(mOldURL);
return NS_OK;
}
NS_IMETHODIMP
nsDOMHashChangeEvent::GetNewURL(nsAString &aURL)
{
aURL.Assign(mNewURL);
return NS_OK;
}
NS_IMETHODIMP
nsDOMHashChangeEvent::InitHashChangeEvent(const nsAString &aTypeArg,
PRBool aCanBubbleArg,
PRBool aCancelableArg,
const nsAString &aOldURL,
const nsAString &aNewURL)
{
nsresult rv = nsDOMEvent::InitEvent(aTypeArg, aCanBubbleArg, aCancelableArg);
NS_ENSURE_SUCCESS(rv, rv);
mOldURL.Assign(aOldURL);
mNewURL.Assign(aNewURL);
return NS_OK;
}
nsresult NS_NewDOMHashChangeEvent(nsIDOMEvent** aInstancePtrResult,
nsPresContext* aPresContext,
nsEvent* aEvent)
{
nsDOMHashChangeEvent* event =
new nsDOMHashChangeEvent(aPresContext, aEvent);
return CallQueryInterface(event, aInstancePtrResult);
}

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

@ -0,0 +1,70 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef nsDOMHashChangeEvent_h__
#define nsDOMHashChangeEvent_h__
class nsEvent;
#include "nsIDOMHashChangeEvent.h"
#include "nsDOMEvent.h"
#include "nsIVariant.h"
#include "nsCycleCollectionParticipant.h"
class nsDOMHashChangeEvent : public nsDOMEvent,
public nsIDOMHashChangeEvent
{
public:
nsDOMHashChangeEvent(nsPresContext* aPresContext, nsEvent* aEvent)
: nsDOMEvent(aPresContext, aEvent)
{
}
virtual ~nsDOMHashChangeEvent();
NS_DECL_ISUPPORTS_INHERITED
NS_DECL_NSIDOMHASHCHANGEEVENT
NS_FORWARD_TO_NSDOMEVENT
protected:
nsString mOldURL;
nsString mNewURL;
};
nsresult NS_NewDOMHashChangeEvent(nsIDOMEvent** aInstancePtrResult,
nsPresContext* aPresContext,
nsEvent* aEvent);
#endif // nsDOMHashChangeEvent_h__

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

@ -50,6 +50,7 @@
#include "nsINode.h" #include "nsINode.h"
#include "nsPIDOMWindow.h" #include "nsPIDOMWindow.h"
#include "nsDOMPopStateEvent.h" #include "nsDOMPopStateEvent.h"
#include "nsDOMHashChangeEvent.h"
#include "nsFrameLoader.h" #include "nsFrameLoader.h"
#define NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH (1 << 0) #define NS_TARGET_CHAIN_FORCE_CONTENT_DISPATCH (1 << 0)
@ -871,6 +872,8 @@ nsEventDispatcher::CreateEvent(nsPresContext* aPresContext,
return NS_NewDOMAudioAvailableEvent(aDOMEvent, aPresContext, nsnull); return NS_NewDOMAudioAvailableEvent(aDOMEvent, aPresContext, nsnull);
if (aEventType.LowerCaseEqualsLiteral("closeevent")) if (aEventType.LowerCaseEqualsLiteral("closeevent"))
return NS_NewDOMCloseEvent(aDOMEvent, aPresContext, nsnull); return NS_NewDOMCloseEvent(aDOMEvent, aPresContext, nsnull);
if (aEventType.LowerCaseEqualsLiteral("hashchangeevent"))
return NS_NewDOMHashChangeEvent(aDOMEvent, aPresContext, nsnull);
return NS_ERROR_DOM_NOT_SUPPORTED_ERR; return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
} }

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

@ -237,6 +237,50 @@ SVGPathData::GetPathSegAtLength(float aDistance) const
return NS_MAX(0U, segIndex - 1); // -1 because while loop takes us 1 too far return NS_MAX(0U, segIndex - 1); // -1 because while loop takes us 1 too far
} }
/**
* The SVG spec says we have to paint stroke caps for zero length subpaths:
*
* http://www.w3.org/TR/SVG11/implnote.html#PathElementImplementationNotes
*
* Cairo only does this for |stroke-linecap: round| and not for
* |stroke-linecap: square| (since that's what Adobe Acrobat has always done).
*
* To help us conform to the SVG spec we have this helper function to draw an
* approximation of square caps for zero length subpaths. It does this by
* inserting a subpath containing a single axis aligned straight line that is
* as small as it can be without cairo throwing it away for being too small to
* affect rendering. Cairo will then draw stroke caps for this axis aligned
* line, creating an axis aligned rectangle (approximating the square that
* would ideally be drawn).
*
* Note that this function inserts a subpath into the current gfx path that
* will be present during both fill and stroke operations.
*/
static void
ApproximateZeroLengthSubpathSquareCaps(const gfxPoint &aPoint, gfxContext *aCtx)
{
// Cairo's fixed point fractional part is 8 bits wide, so its device space
// coordinate granularity is 1/256 pixels. However, to prevent user space
// |aPoint| and |aPoint + tinyAdvance| being rounded to the same device
// coordinates, we double this for |tinyAdvance|:
const gfxSize tinyAdvance = aCtx->DeviceToUser(gfxSize(2.0/256.0, 0.0));
aCtx->MoveTo(aPoint);
aCtx->LineTo(aPoint + gfxPoint(tinyAdvance.width, tinyAdvance.height));
aCtx->MoveTo(aPoint);
}
#define MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS \
do { \
if (capsAreSquare && !subpathHasLength && subpathContainsNonArc && \
SVGPathSegUtils::IsValidType(prevSegType) && \
(!IsMoveto(prevSegType) || \
segType == nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH)) { \
ApproximateZeroLengthSubpathSquareCaps(segStart, aCtx); \
} \
} while(0)
void void
SVGPathData::ConstructPath(gfxContext *aCtx) const SVGPathData::ConstructPath(gfxContext *aCtx) const
{ {
@ -244,9 +288,14 @@ SVGPathData::ConstructPath(gfxContext *aCtx) const
return; // paths without an initial moveto are invalid return; // paths without an initial moveto are invalid
} }
PRBool capsAreSquare = aCtx->CurrentLineCap() == gfxContext::LINE_CAP_SQUARE;
PRBool subpathHasLength = PR_FALSE; // visual length
PRBool subpathContainsNonArc = PR_FALSE;
PRUint32 segType, prevSegType = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN; PRUint32 segType, prevSegType = nsIDOMSVGPathSeg::PATHSEG_UNKNOWN;
gfxPoint pathStart(0.0, 0.0); // start point of [sub]path gfxPoint pathStart(0.0, 0.0); // start point of [sub]path
gfxPoint segEnd(0.0, 0.0); // end point of previous/current segment gfxPoint segStart(0.0, 0.0);
gfxPoint segEnd;
gfxPoint cp1, cp2; // previous bezier's control points gfxPoint cp1, cp2; // previous bezier's control points
gfxPoint tcp1, tcp2; // temporaries gfxPoint tcp1, tcp2; // temporaries
@ -257,36 +306,50 @@ SVGPathData::ConstructPath(gfxContext *aCtx) const
PRUint32 i = 0; PRUint32 i = 0;
while (i < mData.Length()) { while (i < mData.Length()) {
segType = SVGPathSegUtils::DecodeType(mData[i++]); segType = SVGPathSegUtils::DecodeType(mData[i++]);
PRUint32 argCount = SVGPathSegUtils::ArgCountForType(segType);
switch (segType) switch (segType)
{ {
case nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH: case nsIDOMSVGPathSeg::PATHSEG_CLOSEPATH:
// set this early to allow drawing of square caps for "M{x},{y} Z":
subpathContainsNonArc = PR_TRUE;
MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS;
segEnd = pathStart; segEnd = pathStart;
aCtx->ClosePath(); aCtx->ClosePath();
break; break;
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS: case nsIDOMSVGPathSeg::PATHSEG_MOVETO_ABS:
MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS;
pathStart = segEnd = gfxPoint(mData[i], mData[i+1]); pathStart = segEnd = gfxPoint(mData[i], mData[i+1]);
aCtx->MoveTo(segEnd); aCtx->MoveTo(segEnd);
i += 2; subpathHasLength = PR_FALSE;
subpathContainsNonArc = PR_FALSE;
break; break;
case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL: case nsIDOMSVGPathSeg::PATHSEG_MOVETO_REL:
pathStart = segEnd += gfxPoint(mData[i], mData[i+1]); MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS;
pathStart = segEnd = segStart + gfxPoint(mData[i], mData[i+1]);
aCtx->MoveTo(segEnd); aCtx->MoveTo(segEnd);
i += 2; subpathHasLength = PR_FALSE;
subpathContainsNonArc = PR_FALSE;
break; break;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS: case nsIDOMSVGPathSeg::PATHSEG_LINETO_ABS:
segEnd = gfxPoint(mData[i], mData[i+1]); segEnd = gfxPoint(mData[i], mData[i+1]);
aCtx->LineTo(segEnd); aCtx->LineTo(segEnd);
i += 2; if (!subpathHasLength) {
subpathHasLength = (segEnd != segStart);
}
subpathContainsNonArc = PR_TRUE;
break; break;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL: case nsIDOMSVGPathSeg::PATHSEG_LINETO_REL:
segEnd += gfxPoint(mData[i], mData[i+1]); segEnd = segStart + gfxPoint(mData[i], mData[i+1]);
aCtx->LineTo(segEnd); aCtx->LineTo(segEnd);
i += 2; if (!subpathHasLength) {
subpathHasLength = (segEnd != segStart);
}
subpathContainsNonArc = PR_TRUE;
break; break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS: case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_ABS:
@ -294,126 +357,170 @@ SVGPathData::ConstructPath(gfxContext *aCtx) const
cp2 = gfxPoint(mData[i+2], mData[i+3]); cp2 = gfxPoint(mData[i+2], mData[i+3]);
segEnd = gfxPoint(mData[i+4], mData[i+5]); segEnd = gfxPoint(mData[i+4], mData[i+5]);
aCtx->CurveTo(cp1, cp2, segEnd); aCtx->CurveTo(cp1, cp2, segEnd);
i += 6; if (!subpathHasLength) {
subpathHasLength = (segEnd != segStart || segEnd != cp1 || segEnd != cp2);
}
subpathContainsNonArc = PR_TRUE;
break; break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL: case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_REL:
cp1 = segEnd + gfxPoint(mData[i], mData[i+1]); cp1 = segStart + gfxPoint(mData[i], mData[i+1]);
cp2 = segEnd + gfxPoint(mData[i+2], mData[i+3]); cp2 = segStart + gfxPoint(mData[i+2], mData[i+3]);
segEnd += gfxPoint(mData[i+4], mData[i+5]); segEnd = segStart + gfxPoint(mData[i+4], mData[i+5]);
aCtx->CurveTo(cp1, cp2, segEnd); aCtx->CurveTo(cp1, cp2, segEnd);
i += 6; if (!subpathHasLength) {
subpathHasLength = (segEnd != segStart || segEnd != cp1 || segEnd != cp2);
}
subpathContainsNonArc = PR_TRUE;
break; break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS: case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_ABS:
cp1 = gfxPoint(mData[i], mData[i+1]); cp1 = gfxPoint(mData[i], mData[i+1]);
// Convert quadratic curve to cubic curve: // Convert quadratic curve to cubic curve:
tcp1 = segEnd + (cp1 - segEnd) * 2 / 3; tcp1 = segStart + (cp1 - segStart) * 2 / 3;
segEnd = gfxPoint(mData[i+2], mData[i+3]); // changed before setting tcp2! segEnd = gfxPoint(mData[i+2], mData[i+3]); // set before setting tcp2!
tcp2 = cp1 + (segEnd - cp1) / 3; tcp2 = cp1 + (segEnd - cp1) / 3;
aCtx->CurveTo(tcp1, tcp2, segEnd); aCtx->CurveTo(tcp1, tcp2, segEnd);
i += 4; if (!subpathHasLength) {
subpathHasLength = (segEnd != segStart || segEnd != cp1);
}
subpathContainsNonArc = PR_TRUE;
break; break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL: case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_REL:
cp1 = segEnd + gfxPoint(mData[i], mData[i+1]); cp1 = segStart + gfxPoint(mData[i], mData[i+1]);
// Convert quadratic curve to cubic curve: // Convert quadratic curve to cubic curve:
tcp1 = segEnd + (cp1 - segEnd) * 2 / 3; tcp1 = segStart + (cp1 - segStart) * 2 / 3;
segEnd += gfxPoint(mData[i+2], mData[i+3]); // changed before setting tcp2! segEnd = segStart + gfxPoint(mData[i+2], mData[i+3]); // set before setting tcp2!
tcp2 = cp1 + (segEnd - cp1) / 3; tcp2 = cp1 + (segEnd - cp1) / 3;
aCtx->CurveTo(tcp1, tcp2, segEnd); aCtx->CurveTo(tcp1, tcp2, segEnd);
i += 4; if (!subpathHasLength) {
subpathHasLength = (segEnd != segStart || segEnd != cp1);
}
subpathContainsNonArc = PR_TRUE;
break; break;
case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS: case nsIDOMSVGPathSeg::PATHSEG_ARC_ABS:
case nsIDOMSVGPathSeg::PATHSEG_ARC_REL: case nsIDOMSVGPathSeg::PATHSEG_ARC_REL:
{ {
gfxPoint radii(mData[i], mData[i+1]); gfxPoint radii(mData[i], mData[i+1]);
gfxPoint start = segEnd; gfxPoint segEnd = gfxPoint(mData[i+5], mData[i+6]);
gfxPoint end = gfxPoint(mData[i+5], mData[i+6]);
if (segType == nsIDOMSVGPathSeg::PATHSEG_ARC_REL) { if (segType == nsIDOMSVGPathSeg::PATHSEG_ARC_REL) {
end += start; segEnd += segStart;
} }
segEnd = end; if (segEnd != segStart) {
if (start != end) {
if (radii.x == 0.0f || radii.y == 0.0f) { if (radii.x == 0.0f || radii.y == 0.0f) {
aCtx->LineTo(end); aCtx->LineTo(segEnd);
i += 7; } else {
break; nsSVGArcConverter converter(segStart, segEnd, radii, mData[i+2],
} mData[i+3] != 0, mData[i+4] != 0);
nsSVGArcConverter converter(start, end, radii, mData[i+2], while (converter.GetNextSegment(&cp1, &cp2, &segEnd)) {
mData[i+3] != 0, mData[i+4] != 0); aCtx->CurveTo(cp1, cp2, segEnd);
while (converter.GetNextSegment(&cp1, &cp2, &end)) { }
aCtx->CurveTo(cp1, cp2, end);
} }
} }
i += 7; if (!subpathHasLength) {
subpathHasLength = (segEnd != segStart);
}
break; break;
} }
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS: case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_ABS:
segEnd = gfxPoint(mData[i++], segEnd.y); segEnd = gfxPoint(mData[i], segStart.y);
aCtx->LineTo(segEnd); aCtx->LineTo(segEnd);
if (!subpathHasLength) {
subpathHasLength = (segEnd != segStart);
}
subpathContainsNonArc = PR_TRUE;
break; break;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL: case nsIDOMSVGPathSeg::PATHSEG_LINETO_HORIZONTAL_REL:
segEnd += gfxPoint(mData[i++], 0.0f); segEnd = segStart + gfxPoint(mData[i], 0.0f);
aCtx->LineTo(segEnd); aCtx->LineTo(segEnd);
if (!subpathHasLength) {
subpathHasLength = (segEnd != segStart);
}
subpathContainsNonArc = PR_TRUE;
break; break;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS: case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_ABS:
segEnd = gfxPoint(segEnd.x, mData[i++]); segEnd = gfxPoint(segStart.x, mData[i]);
aCtx->LineTo(segEnd); aCtx->LineTo(segEnd);
if (!subpathHasLength) {
subpathHasLength = (segEnd != segStart);
}
subpathContainsNonArc = PR_TRUE;
break; break;
case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL: case nsIDOMSVGPathSeg::PATHSEG_LINETO_VERTICAL_REL:
segEnd += gfxPoint(0.0f, mData[i++]); segEnd = segStart + gfxPoint(0.0f, mData[i]);
aCtx->LineTo(segEnd); aCtx->LineTo(segEnd);
if (!subpathHasLength) {
subpathHasLength = (segEnd != segStart);
}
subpathContainsNonArc = PR_TRUE;
break; break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS: case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_ABS:
cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segEnd * 2 - cp2 : segEnd; cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segStart * 2 - cp2 : segStart;
cp2 = gfxPoint(mData[i], mData[i+1]); cp2 = gfxPoint(mData[i], mData[i+1]);
segEnd = gfxPoint(mData[i+2], mData[i+3]); segEnd = gfxPoint(mData[i+2], mData[i+3]);
aCtx->CurveTo(cp1, cp2, segEnd); aCtx->CurveTo(cp1, cp2, segEnd);
i += 4; if (!subpathHasLength) {
subpathHasLength = (segEnd != segStart || segEnd != cp1 || segEnd != cp2);
}
subpathContainsNonArc = PR_TRUE;
break; break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL: case nsIDOMSVGPathSeg::PATHSEG_CURVETO_CUBIC_SMOOTH_REL:
cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segEnd * 2 - cp2 : segEnd; cp1 = SVGPathSegUtils::IsCubicType(prevSegType) ? segStart * 2 - cp2 : segStart;
cp2 = segEnd + gfxPoint(mData[i], mData[i+1]); cp2 = segStart + gfxPoint(mData[i], mData[i+1]);
segEnd += gfxPoint(mData[i+2], mData[i+3]); segEnd = segStart + gfxPoint(mData[i+2], mData[i+3]);
aCtx->CurveTo(cp1, cp2, segEnd); aCtx->CurveTo(cp1, cp2, segEnd);
i += 4; if (!subpathHasLength) {
subpathHasLength = (segEnd != segStart || segEnd != cp1 || segEnd != cp2);
}
subpathContainsNonArc = PR_TRUE;
break; break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS: case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_ABS:
cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segEnd * 2 - cp1 : segEnd; cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segStart * 2 - cp1 : segStart;
// Convert quadratic curve to cubic curve: // Convert quadratic curve to cubic curve:
tcp1 = segEnd + (cp1 - segEnd) * 2 / 3; tcp1 = segStart + (cp1 - segStart) * 2 / 3;
segEnd = gfxPoint(mData[i], mData[i+1]); // changed before setting tcp2! segEnd = gfxPoint(mData[i], mData[i+1]); // set before setting tcp2!
tcp2 = cp1 + (segEnd - cp1) / 3; tcp2 = cp1 + (segEnd - cp1) / 3;
aCtx->CurveTo(tcp1, tcp2, segEnd); aCtx->CurveTo(tcp1, tcp2, segEnd);
i += 2; if (!subpathHasLength) {
subpathHasLength = (segEnd != segStart || segEnd != cp1);
}
subpathContainsNonArc = PR_TRUE;
break; break;
case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL: case nsIDOMSVGPathSeg::PATHSEG_CURVETO_QUADRATIC_SMOOTH_REL:
cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segEnd * 2 - cp1 : segEnd; cp1 = SVGPathSegUtils::IsQuadraticType(prevSegType) ? segStart * 2 - cp1 : segStart;
// Convert quadratic curve to cubic curve: // Convert quadratic curve to cubic curve:
tcp1 = segEnd + (cp1 - segEnd) * 2 / 3; tcp1 = segStart + (cp1 - segStart) * 2 / 3;
segEnd = segEnd + gfxPoint(mData[i], mData[i+1]); // changed before setting tcp2! segEnd = segStart + gfxPoint(mData[i], mData[i+1]); // changed before setting tcp2!
tcp2 = cp1 + (segEnd - cp1) / 3; tcp2 = cp1 + (segEnd - cp1) / 3;
aCtx->CurveTo(tcp1, tcp2, segEnd); aCtx->CurveTo(tcp1, tcp2, segEnd);
i += 2; if (!subpathHasLength) {
subpathHasLength = (segEnd != segStart || segEnd != cp1);
}
subpathContainsNonArc = PR_TRUE;
break; break;
default: default:
NS_NOTREACHED("Bad path segment type"); NS_NOTREACHED("Bad path segment type");
return; // according to spec we'd use everything up to the bad seg anyway return; // according to spec we'd use everything up to the bad seg anyway
} }
i += argCount;
prevSegType = segType; prevSegType = segType;
segStart = segEnd;
} }
NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt"); NS_ABORT_IF_FALSE(i == mData.Length(), "Very, very bad - mData corrupt");
MAYBE_APPROXIMATE_ZERO_LENGTH_SUBPATH_SQUARE_CAPS;
} }
already_AddRefed<gfxFlattenedPath> already_AddRefed<gfxFlattenedPath>

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

@ -79,6 +79,7 @@ _TEST_FILES = \
test_SVGAnimatedImageSMILDisabled.html \ test_SVGAnimatedImageSMILDisabled.html \
animated-svg-image-helper.html \ animated-svg-image-helper.html \
animated-svg-image-helper.svg \ animated-svg-image-helper.svg \
test_stroke-linecap-hit-testing.xhtml \
test_SVGLengthList.xhtml \ test_SVGLengthList.xhtml \
test_SVGLengthList-2.xhtml \ test_SVGLengthList-2.xhtml \
test_SVGPathSegList.xhtml \ test_SVGPathSegList.xhtml \

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

@ -0,0 +1,48 @@
<html xmlns="http://www.w3.org/1999/xhtml">
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=589648
-->
<head>
<title>Test hit-testing of line caps</title>
<script type="text/javascript" src="/MochiKit/packed.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css" />
</head>
<body onload="run()">
<script class="testbody" type="text/javascript">
<![CDATA[
SimpleTest.waitForExplicitFinish();
function run()
{
var svg = document.getElementById('svg');
var div = document.getElementById("div");
var x = div.offsetLeft;
var y = div.offsetTop;
var got, expected;
got = document.elementFromPoint(5 + x, 5 + y);
expected = document.getElementById('zero-length-square-caps');
is(got, expected, 'Check hit on zero length subpath\'s square caps');
SimpleTest.finish();
}
]]>
</script>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=500174">Mozilla Bug 500174</a>
<p id="display"></p>
<div id="content">
<div width="100%" height="1" id="div"></div>
<svg xmlns="http://www.w3.org/2000/svg" id="svg" width="400" height="300">
<path id="zero-length-square-caps" stroke="blue" stroke-width="50"
stroke-linecap="square" d="M25,25 L25,25"/>
</svg>
</div>
<pre id="test">
</pre>
</body>
</html>

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

@ -215,6 +215,7 @@
#endif #endif
#include "nsPluginError.h" #include "nsPluginError.h"
#include "nsContentUtils.h"
static NS_DEFINE_CID(kDOMScriptObjectFactoryCID, static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
NS_DOM_SCRIPT_OBJECT_FACTORY_CID); NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
@ -8215,33 +8216,6 @@ nsDocShell::InternalLoad(nsIURI * aURI,
mAllowKeywordFixup = mAllowKeywordFixup =
(aFlags & INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) != 0; (aFlags & INTERNAL_LOAD_FLAGS_ALLOW_THIRD_PARTY_FIXUP) != 0;
mURIResultedInDocument = PR_FALSE; // reset the clock... mURIResultedInDocument = PR_FALSE; // reset the clock...
//
// First:
// Check to see if the new URI is an anchor in the existing document.
// Skip this check if we're doing some sort of abnormal load, if the
// new load is a non-history load and has postdata, or if we're doing
// a history load and the page identifiers of mOSHE and aSHEntry
// don't match.
//
PRBool allowScroll = PR_TRUE;
if (!aSHEntry) {
allowScroll = (aPostData == nsnull);
} else if (mOSHE) {
PRUint32 ourPageIdent;
mOSHE->GetPageIdentifier(&ourPageIdent);
PRUint32 otherPageIdent;
aSHEntry->GetPageIdentifier(&otherPageIdent);
allowScroll = (ourPageIdent == otherPageIdent);
#ifdef DEBUG
if (allowScroll) {
nsCOMPtr<nsIInputStream> currentPostData;
mOSHE->GetPostData(getter_AddRefs(currentPostData));
NS_ASSERTION(currentPostData == aPostData,
"Different POST data for entries for the same page?");
}
#endif
}
if (aLoadType == LOAD_NORMAL || if (aLoadType == LOAD_NORMAL ||
aLoadType == LOAD_STOP_CONTENT || aLoadType == LOAD_STOP_CONTENT ||
@ -8249,40 +8223,81 @@ nsDocShell::InternalLoad(nsIURI * aURI,
aLoadType == LOAD_HISTORY || aLoadType == LOAD_HISTORY ||
aLoadType == LOAD_LINK) { aLoadType == LOAD_LINK) {
PRBool wasAnchor = PR_FALSE; // Split mCurrentURI and aURI on the '#' character. Make sure we read
PRBool doHashchange = PR_FALSE; // the return values of SplitURIAtHash; it might fail because
// mCurrentURI is null, for instance, and we don't want to allow a
// short-circuited navigation in that case.
nsCAutoString curBeforeHash, curHash, newBeforeHash, newHash;
nsresult splitRv1, splitRv2;
splitRv1 = nsContentUtils::SplitURIAtHash(mCurrentURI, curBeforeHash, curHash);
splitRv2 = nsContentUtils::SplitURIAtHash(aURI, newBeforeHash, newHash);
// Get the position of the scrollers. PRBool sameExceptHashes = NS_SUCCEEDED(splitRv1) &&
nscoord cx = 0, cy = 0; NS_SUCCEEDED(splitRv2) &&
GetCurScrollPos(ScrollOrientation_X, &cx); curBeforeHash.Equals(newBeforeHash);
GetCurScrollPos(ScrollOrientation_Y, &cy);
if (allowScroll) {
NS_ENSURE_SUCCESS(ScrollIfAnchor(aURI, &wasAnchor, aLoadType,
&doHashchange),
NS_ERROR_FAILURE);
}
// If this is a history load, aSHEntry will have document identifier X
// if it was created as a result of a History.pushState() from a
// SHEntry with doc ident X, or if it was created by changing the hash
// of the URI corresponding to a SHEntry with doc ident X.
PRBool sameDocIdent = PR_FALSE; PRBool sameDocIdent = PR_FALSE;
if (mOSHE && aSHEntry) { if (mOSHE && aSHEntry) {
PRUint64 ourDocIdent, otherDocIdent; // We're doing a history load.
mOSHE->GetDocIdentifier(&ourDocIdent);
aSHEntry->GetDocIdentifier(&otherDocIdent); PRUint64 ourDocIdent, otherDocIdent;
sameDocIdent = (ourDocIdent == otherDocIdent); mOSHE->GetDocIdentifier(&ourDocIdent);
aSHEntry->GetDocIdentifier(&otherDocIdent);
sameDocIdent = (ourDocIdent == otherDocIdent);
#ifdef DEBUG
if (sameDocIdent) {
nsCOMPtr<nsIInputStream> currentPostData;
mOSHE->GetPostData(getter_AddRefs(currentPostData));
NS_ASSERTION(currentPostData == aPostData,
"Different POST data for entries for the same page?");
}
#endif
} }
// Do a short-circuited load if the new URI differs from the current // A short-circuited load happens when we navigate between two SHEntries
// URI only in the hash, or if the two entries belong to the same // for the same document. We do a short-circuited load under two
// document and don't point to the same object. // circumstances. Either
// //
// (If we didn't check that the SHEntries are different objects, // a) we're navigating between two different SHEntries which have the
// history.go(0) would short-circuit instead of triggering a true // same document identifiers, or
// load, and we wouldn't dispatch an onload event to the page.) //
if (wasAnchor || (sameDocIdent && (mOSHE != aSHEntry))) { // b) we're navigating to a new shentry whose URI differs from the
// current URI only in its hash, the new hash is non-empty, and
// we're not doing a POST.
//
// The restriction tha the SHEntries in (a) must be different ensures
// that history.go(0) and the like trigger full refreshes, rather than
// short-circuited loads.
PRBool doShortCircuitedLoad = (sameDocIdent && mOSHE != aSHEntry) ||
(!aSHEntry && aPostData == nsnull &&
sameExceptHashes && !newHash.IsEmpty());
// Fire a hashchange event if we're doing a short-circuited load and the
// URIs differ only in their hashes.
PRBool doHashchange = doShortCircuitedLoad &&
sameExceptHashes &&
!curHash.Equals(newHash);
if (doShortCircuitedLoad) {
// Save the current URI; we need it if we fire a hashchange later.
nsCOMPtr<nsIURI> oldURI = mCurrentURI;
// Save the position of the scrollers.
nscoord cx = 0, cy = 0;
GetCurScrollPos(ScrollOrientation_X, &cx);
GetCurScrollPos(ScrollOrientation_Y, &cy);
// We scroll the window precisely when we fire a hashchange event.
if (doHashchange) {
// Take the '#' off the hashes before passing them to
// ScrollToAnchor.
nsDependentCSubstring curHashName(curHash, 1);
nsDependentCSubstring newHashName(newHash, 1);
rv = ScrollToAnchor(curHashName, newHashName, aLoadType);
NS_ENSURE_SUCCESS(rv, rv);
}
mLoadType = aLoadType; mLoadType = aLoadType;
mURIResultedInDocument = PR_TRUE; mURIResultedInDocument = PR_TRUE;
@ -8307,7 +8322,7 @@ nsDocShell::InternalLoad(nsIURI * aURI,
OnNewURI(aURI, nsnull, owner, mLoadType, PR_TRUE, PR_TRUE, PR_TRUE); OnNewURI(aURI, nsnull, owner, mLoadType, PR_TRUE, PR_TRUE, PR_TRUE);
nsCOMPtr<nsIInputStream> postData; nsCOMPtr<nsIInputStream> postData;
PRUint32 pageIdent = PR_UINT32_MAX; PRUint64 docIdent = PRUint64(-1);
nsCOMPtr<nsISupports> cacheKey; nsCOMPtr<nsISupports> cacheKey;
if (mOSHE) { if (mOSHE) {
@ -8321,20 +8336,9 @@ nsDocShell::InternalLoad(nsIURI * aURI,
// wouldn't want here. // wouldn't want here.
if (aLoadType & LOAD_CMD_NORMAL) { if (aLoadType & LOAD_CMD_NORMAL) {
mOSHE->GetPostData(getter_AddRefs(postData)); mOSHE->GetPostData(getter_AddRefs(postData));
mOSHE->GetPageIdentifier(&pageIdent); mOSHE->GetDocIdentifier(&docIdent);
mOSHE->GetCacheKey(getter_AddRefs(cacheKey)); mOSHE->GetCacheKey(getter_AddRefs(cacheKey));
} }
if (mLSHE && wasAnchor) {
// If it's an anchor load, set mLSHE's doc identifier to
// mOSHE's doc identifier -- These are the same documents,
// as far as HTML5 is concerned.
PRUint64 docIdent;
rv = mOSHE->GetDocIdentifier(&docIdent);
if (NS_SUCCEEDED(rv)) {
mLSHE->SetDocIdentifier(docIdent);
}
}
} }
/* Assign mOSHE to mLSHE. This will either be a new entry created /* Assign mOSHE to mLSHE. This will either be a new entry created
@ -8354,10 +8358,10 @@ nsDocShell::InternalLoad(nsIURI * aURI,
if (cacheKey) if (cacheKey)
mOSHE->SetCacheKey(cacheKey); mOSHE->SetCacheKey(cacheKey);
// Propagate our page ident to the new mOSHE so that // Propagate our document identifier to the new mOSHE so that
// we'll know it just differed by a scroll on the page. // we'll know it's related by an anchor navigation or pushState.
if (pageIdent != PR_UINT32_MAX) if (docIdent != PRUint64(-1))
mOSHE->SetPageIdentifier(pageIdent); mOSHE->SetDocIdentifier(docIdent);
} }
/* restore previous position of scroller(s), if we're moving /* restore previous position of scroller(s), if we're moving
@ -8425,8 +8429,11 @@ nsDocShell::InternalLoad(nsIURI * aURI,
window->DispatchSyncPopState(); window->DispatchSyncPopState();
} }
if (doHashchange) if (doHashchange) {
window->DispatchAsyncHashchange(); // Make sure to use oldURI here, not mCurrentURI, because by
// now, mCurrentURI has changed!
window->DispatchAsyncHashchange(oldURI, aURI);
}
} }
return NS_OK; return NS_OK;
@ -9052,20 +9059,9 @@ nsresult nsDocShell::DoChannelLoad(nsIChannel * aChannel,
} }
nsresult nsresult
nsDocShell::ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor, nsDocShell::ScrollToAnchor(nsACString & aCurHash, nsACString & aNewHash,
PRUint32 aLoadType, PRBool * aDoHashchange) PRUint32 aLoadType)
{ {
NS_ASSERTION(aURI, "null uri arg");
NS_ASSERTION(aWasAnchor, "null anchor arg");
NS_PRECONDITION(aDoHashchange, "null hashchange arg");
if (!aURI || !aWasAnchor) {
return NS_ERROR_FAILURE;
}
*aWasAnchor = PR_FALSE;
*aDoHashchange = PR_FALSE;
if (!mCurrentURI) { if (!mCurrentURI) {
return NS_OK; return NS_OK;
} }
@ -9075,116 +9071,28 @@ nsDocShell::ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor,
if (NS_FAILED(rv) || !shell) { if (NS_FAILED(rv) || !shell) {
// If we failed to get the shell, or if there is no shell, // If we failed to get the shell, or if there is no shell,
// nothing left to do here. // nothing left to do here.
return rv; return rv;
} }
// NOTE: we assume URIs are absolute for comparison purposes
nsCAutoString currentSpec;
NS_ENSURE_SUCCESS(mCurrentURI->GetSpec(currentSpec),
NS_ERROR_FAILURE);
nsCAutoString newSpec;
NS_ENSURE_SUCCESS(aURI->GetSpec(newSpec), NS_ERROR_FAILURE);
// Search for hash marks in the current URI and the new URI and
// take a copy of everything to the left of the hash for
// comparison.
const char kHash = '#';
// Split the new URI into a left and right part
// (assume we're parsing it out right)
nsACString::const_iterator urlStart, urlEnd, refStart, refEnd;
newSpec.BeginReading(urlStart);
newSpec.EndReading(refEnd);
PRInt32 hashNew = newSpec.FindChar(kHash);
if (hashNew == 0) {
return NS_OK; // Strange URI
}
if (hashNew > 0) {
// found it
urlEnd = urlStart;
urlEnd.advance(hashNew);
refStart = urlEnd;
++refStart; // advanced past '#'
}
else {
// no hash at all
urlEnd = refStart = refEnd;
}
const nsACString& sNewLeft = Substring(urlStart, urlEnd);
const nsACString& sNewRef = Substring(refStart, refEnd);
// Split the current URI in a left and right part
nsACString::const_iterator currentLeftStart, currentLeftEnd,
currentRefStart, currentRefEnd;
currentSpec.BeginReading(currentLeftStart);
currentSpec.EndReading(currentRefEnd);
PRInt32 hashCurrent = currentSpec.FindChar(kHash);
if (hashCurrent == 0) {
return NS_OK; // Strange URI
}
if (hashCurrent > 0) {
currentLeftEnd = currentLeftStart;
currentLeftEnd.advance(hashCurrent);
currentRefStart = currentLeftEnd;
++currentRefStart; // advance past '#'
}
else {
// no hash at all in currentSpec
currentLeftEnd = currentRefStart = currentRefEnd;
}
// If we have no new anchor, we do not want to scroll, unless there is a // If we have no new anchor, we do not want to scroll, unless there is a
// current anchor and we are doing a history load. So return if we have no // current anchor and we are doing a history load. So return if we have no
// new anchor, and there is no current anchor or the load is not a history // new anchor, and there is no current anchor or the load is not a history
// load. // load.
NS_ASSERTION(hashNew != 0 && hashCurrent != 0, if ((aCurHash.IsEmpty() || aLoadType != LOAD_HISTORY) &&
"What happened to the early returns above?"); aNewHash.IsEmpty()) {
if (hashNew == kNotFound &&
(hashCurrent == kNotFound || aLoadType != LOAD_HISTORY)) {
return NS_OK; return NS_OK;
} }
// Compare the URIs.
//
// NOTE: this is a case sensitive comparison because some parts of the
// URI are case sensitive, and some are not. i.e. the domain name
// is case insensitive but the the paths are not.
//
// This means that comparing "http://www.ABC.com/" to "http://www.abc.com/"
// will fail this test.
if (!Substring(currentLeftStart, currentLeftEnd).Equals(sNewLeft)) {
return NS_OK; // URIs not the same
}
// Now we know we are dealing with an anchor
*aWasAnchor = PR_TRUE;
// We should fire a hashchange event once we're done here if the two hashes
// are different.
*aDoHashchange = !Substring(currentRefStart, currentRefEnd).Equals(sNewRef);
// Both the new and current URIs refer to the same page. We can now // Both the new and current URIs refer to the same page. We can now
// browse to the hash stored in the new URI. // browse to the hash stored in the new URI.
if (!sNewRef.IsEmpty()) { if (!aNewHash.IsEmpty()) {
// anchor is there, but if it's a load from history, // anchor is there, but if it's a load from history,
// we don't have any anchor jumping to do // we don't have any anchor jumping to do
PRBool scroll = aLoadType != LOAD_HISTORY && PRBool scroll = aLoadType != LOAD_HISTORY &&
aLoadType != LOAD_RELOAD_NORMAL; aLoadType != LOAD_RELOAD_NORMAL;
char *str = ToNewCString(sNewRef); char *str = ToNewCString(aNewHash);
if (!str) { if (!str) {
return NS_ERROR_OUT_OF_MEMORY; return NS_ERROR_OUT_OF_MEMORY;
} }
@ -9226,7 +9134,7 @@ nsDocShell::ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor,
nsXPIDLString uStr; nsXPIDLString uStr;
rv = textToSubURI->UnEscapeAndConvert(PromiseFlatCString(aCharset).get(), rv = textToSubURI->UnEscapeAndConvert(PromiseFlatCString(aCharset).get(),
PromiseFlatCString(sNewRef).get(), PromiseFlatCString(aNewHash).get(),
getter_Copies(uStr)); getter_Copies(uStr));
NS_ENSURE_SUCCESS(rv, rv); NS_ENSURE_SUCCESS(rv, rv);

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

@ -332,8 +332,8 @@ protected:
nsIURILoader * aURILoader, nsIURILoader * aURILoader,
PRBool aBypassClassifier); PRBool aBypassClassifier);
nsresult ScrollIfAnchor(nsIURI * aURI, PRBool * aWasAnchor, nsresult ScrollToAnchor(nsACString & curHash, nsACString & newHash,
PRUint32 aLoadType, PRBool * aDoHashchange); PRUint32 aLoadType);
// Tries to stringify a given variant by converting it to JSON. This only // Tries to stringify a given variant by converting it to JSON. This only
// works if the variant is backed by a JSVal. // works if the variant is backed by a JSVal.

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

@ -58,7 +58,7 @@ class nsDocShellEditorData;
[ptr] native nsDocShellEditorDataPtr(nsDocShellEditorData); [ptr] native nsDocShellEditorDataPtr(nsDocShellEditorData);
[scriptable, uuid(39b73c3a-48eb-4189-8069-247279c3c42d)] [scriptable, uuid(5f3ebf43-6944-45fb-b1b1-78a05bf9370b)]
interface nsISHEntry : nsIHistoryEntry interface nsISHEntry : nsIHistoryEntry
{ {
/** URI for the document */ /** URI for the document */
@ -139,22 +139,12 @@ interface nsISHEntry : nsIHistoryEntry
*/ */
attribute unsigned long ID; attribute unsigned long ID;
/**
* pageIdentifier is an integer that should be the same for two entries
* attached to the same docshell only if the two entries are entries for
* the same page in the sense that one could go from the state represented
* by one to the state represented by the other simply by scrolling (so the
* entries are separated by an anchor traversal or a subframe navigation in
* some other frame).
*/
attribute unsigned long pageIdentifier;
/** /**
* docIdentifier is an integer that should be the same for two entries * docIdentifier is an integer that should be the same for two entries
* attached to the same docshell if and only if the two entries are entries * attached to the same docshell if and only if the two entries are entries
* for the same document. In practice, two entries A and B will have the * for the same document. In practice, two entries A and B will have the
* same docIdentifier if they have the same pageIdentifier or if B was * same docIdentifier if we arrived at B by clicking an anchor link in A or
* created by A calling history.pushState(). * if B was created by A's calling history.pushState().
*/ */
attribute unsigned long long docIdentifier; attribute unsigned long long docIdentifier;

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

@ -105,7 +105,6 @@ static void StopTrackingEntry(nsSHEntry *aEntry)
nsSHEntry::nsSHEntry() nsSHEntry::nsSHEntry()
: mLoadType(0) : mLoadType(0)
, mID(gEntryID++) , mID(gEntryID++)
, mPageIdentifier(mID)
, mDocIdentifier(gEntryDocIdentifier++) , mDocIdentifier(gEntryDocIdentifier++)
, mScrollPositionX(0) , mScrollPositionX(0)
, mScrollPositionY(0) , mScrollPositionY(0)
@ -130,7 +129,6 @@ nsSHEntry::nsSHEntry(const nsSHEntry &other)
, mLayoutHistoryState(other.mLayoutHistoryState) , mLayoutHistoryState(other.mLayoutHistoryState)
, mLoadType(0) // XXX why not copy? , mLoadType(0) // XXX why not copy?
, mID(other.mID) , mID(other.mID)
, mPageIdentifier(other.mPageIdentifier)
, mDocIdentifier(other.mDocIdentifier) , mDocIdentifier(other.mDocIdentifier)
, mScrollPositionX(0) // XXX why not copy? , mScrollPositionX(0) // XXX why not copy?
, mScrollPositionY(0) // XXX why not copy? , mScrollPositionY(0) // XXX why not copy?
@ -391,18 +389,6 @@ NS_IMETHODIMP nsSHEntry::SetID(PRUint32 aID)
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP nsSHEntry::GetPageIdentifier(PRUint32 * aResult)
{
*aResult = mPageIdentifier;
return NS_OK;
}
NS_IMETHODIMP nsSHEntry::SetPageIdentifier(PRUint32 aPageIdentifier)
{
mPageIdentifier = aPageIdentifier;
return NS_OK;
}
NS_IMETHODIMP nsSHEntry::GetDocIdentifier(PRUint64 * aResult) NS_IMETHODIMP nsSHEntry::GetDocIdentifier(PRUint64 * aResult)
{ {
*aResult = mDocIdentifier; *aResult = mDocIdentifier;

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

@ -99,7 +99,6 @@ private:
nsCOMArray<nsISHEntry> mChildren; nsCOMArray<nsISHEntry> mChildren;
PRUint32 mLoadType; PRUint32 mLoadType;
PRUint32 mID; PRUint32 mID;
PRUint32 mPageIdentifier;
PRInt64 mDocIdentifier; PRInt64 mDocIdentifier;
PRInt32 mScrollPositionX; PRInt32 mScrollPositionX;
PRInt32 mScrollPositionY; PRInt32 mScrollPositionY;

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

@ -97,6 +97,9 @@ _TEST_FILES = \
test_bug634834.html \ test_bug634834.html \
file_bug634834.html \ file_bug634834.html \
test_bug637644.html \ test_bug637644.html \
test_bug640387_1.html \
test_bug640387_2.html \
file_bug640387.html \
test_framedhistoryframes.html \ test_framedhistoryframes.html \
test_windowedhistoryframes.html \ test_windowedhistoryframes.html \
historyframes.html \ historyframes.html \

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

@ -0,0 +1,26 @@
<html>
<body onhashchange='hashchange()' onload='load()' onpopstate='popstate()'>
<script>
function hashchange() {
var f = (opener || parent).childHashchange;
if (f)
f();
}
function load() {
var f = (opener || parent).childLoad;
if (f)
f();
}
function popstate() {
var f = (opener || parent).childPopstate;
if (f)
f();
}
</script>
Not much to see here...
</body>
</html>

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

@ -135,10 +135,6 @@ function run_test() {
yield; yield;
eventExpected("Going forward should trigger a hashchange."); eventExpected("Going forward should trigger a hashchange.");
frameCw.window.location.hash = "";
yield;
eventExpected("Changing window.location.hash should trigger a hashchange.");
// window.location has a trailing '#' right now, so we append "link1", not // window.location has a trailing '#' right now, so we append "link1", not
// "#link1". // "#link1".
frameCw.window.location = frameCw.window.location + "link1"; frameCw.window.location = frameCw.window.location + "link1";
@ -187,8 +183,8 @@ function run_test() {
"Event type should be 'hashchange'."); "Event type should be 'hashchange'.");
is(gSampleEvent.cancelable, false, is(gSampleEvent.cancelable, false,
"The hashchange event shouldn't be cancelable."); "The hashchange event shouldn't be cancelable.");
is(gSampleEvent.bubbles, false, is(gSampleEvent.bubbles, true,
"The hashchange event shouldn't bubble."); "The hashchange event should bubble.");
/* /*
* TEST 3 tests that: * TEST 3 tests that:

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

@ -0,0 +1,110 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=640387
-->
<head>
<title>Test for Bug 640387</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.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=640387">Mozilla Bug 640387</a>
<script type='application/javascript;version=1.7'>
SimpleTest.waitForExplicitFinish();
function test() {
/* Spin the event loop so we get out of the onload handler. */
SimpleTest.executeSoon(function() { gGen.next() });
yield;
popup.history.pushState('', '', '#hash1');
popup.history.pushState('', '', '#hash2');
// Now the history looks like:
// file_bug640387.html
// file_bug640387.html#hash1
// file_bug640387.html#hash2 <-- current
// Going back should trigger a hashchange, which will wake us up from the
// yield.
popup.history.back();
yield;
ok(true, 'Got first hashchange.');
// Going back should wake us up again.
popup.history.back();
yield;
ok(true, 'Got second hashchange.');
// Now the history looks like:
// file_bug640387.html <-- current
// file_bug640387.html#hash1
// file_bug640387.html#hash2
// Going forward should trigger a hashchange.
popup.history.forward();
yield;
ok(true, 'Got third hashchange.');
// Now modify the history so it looks like:
// file_bug640387.html
// file_bug640387.html#hash1
// file_bug640387.html#hash1 <-- current
popup.history.pushState('', '', '#hash1');
// Now when we go back, we should not get a hashchange. Instead, wait for a
// popstate. We need to asynchronously go back because popstate is fired
// sync.
gHashchangeExpected = false;
gCallbackOnPopstate = true;
SimpleTest.executeSoon(function() { popup.history.back() });
yield;
ok(true, 'Got popstate.');
gCallbackOnPopstate = false;
// Spin the event loop so hashchange has a chance to fire, if it's going to.
SimpleTest.executeSoon(function() { gGen.next() });
yield;
popup.close();
SimpleTest.finish();
yield;
}
gGen = null;
function childLoad() {
gGen = test();
gGen.next();
}
gHashchangeExpected = true;
function childHashchange() {
if (gHashchangeExpected) {
gGen.next();
}
else {
ok(false, "Got hashchange when we weren't expecting one.");
}
}
gCallbackOnPopstate = false;
function childPopstate() {
if (gCallbackOnPopstate) {
gGen.next();
}
}
/* We need to run this test in a popup, because navigating an iframe
* back/forwards tends to cause intermittent orange. */
popup = window.open('file_bug640387.html');
/* Control now flows up to childLoad(), called once the popup loads. */
</script>
</body>
</html>

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

@ -0,0 +1,91 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=640387
-->
<head>
<title>Test for Bug 640387</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.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=640387">Mozilla Bug 640387</a>
<!-- Test that, when going from
http://example.com/#foo
to
http://example.com/
via a non-history load, we do a true load, rather than a scroll. -->
<script type='application/javascript;version=1.7'>
SimpleTest.waitForExplicitFinish();
callbackOnLoad = false;
function childLoad() {
if (callbackOnLoad) {
callbackOnLoad = false;
gGen.next();
}
}
errorOnHashchange = false;
callbackOnHashchange = false;
function childHashchange() {
if (errorOnHashchange) {
ok(false, 'Got unexpected hashchange.');
}
if (callbackOnHashchange) {
callbackOnHashchange = false;
gGen.next();
}
}
function run_test() {
var iframe = $('iframe').contentWindow;
ok(true, 'Got first load');
// Spin the event loop so we exit the onload handler.
SimpleTest.executeSoon(function() { gGen.next() });
yield;
let origLocation = iframe.location + '';
callbackOnHashchange = true;
iframe.location.hash = '#1';
// Wait for a hashchange event.
yield;
ok(true, 'Got hashchange.');
iframe.location = origLocation;
// This should produce a load event and *not* a hashchange, because the
// result of the load is a different document than we had previously.
callbackOnLoad = true;
errorOnHashchange = true;
yield;
ok(true, 'Got final load.');
// Spin the event loop to give hashchange a chance to fire, if it's going to.
SimpleTest.executeSoon(function() { gGen.next() });
yield;
SimpleTest.finish();
yield;
}
callbackOnLoad = true;
gGen = run_test();
</script>
<iframe id='iframe' src='file_bug640387.html'></iframe>
</body>
</html>

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

@ -87,6 +87,7 @@
#include "nsIDOMKeyEvent.h" #include "nsIDOMKeyEvent.h"
#include "nsIDOMEventListener.h" #include "nsIDOMEventListener.h"
#include "nsIDOMPopStateEvent.h" #include "nsIDOMPopStateEvent.h"
#include "nsIDOMHashChangeEvent.h"
#include "nsContentUtils.h" #include "nsContentUtils.h"
#include "nsDOMWindowUtils.h" #include "nsDOMWindowUtils.h"
#include "nsIDOMGlobalPropertyInitializer.h" #include "nsIDOMGlobalPropertyInitializer.h"
@ -1440,6 +1441,8 @@ static nsDOMClassInfoData sClassInfoData[] = {
DOM_DEFAULT_SCRIPTABLE_FLAGS) DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(PopStateEvent, nsDOMGenericSH, NS_DEFINE_CLASSINFO_DATA(PopStateEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS) DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(HashChangeEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(EventListenerInfo, nsDOMGenericSH, NS_DEFINE_CLASSINFO_DATA(EventListenerInfo, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS) DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(TransitionEvent, nsDOMGenericSH, NS_DEFINE_CLASSINFO_DATA(TransitionEvent, nsDOMGenericSH,
@ -2580,6 +2583,11 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_EVENT_MAP_ENTRIES DOM_CLASSINFO_EVENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(HashChangeEvent, nsIDOMHashChangeEvent)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMHashChangeEvent)
DOM_CLASSINFO_DOCUMENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(HTMLDocument, nsIDOMHTMLDocument) DOM_CLASSINFO_MAP_BEGIN(HTMLDocument, nsIDOMHTMLDocument)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLDocument) DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLDocument)
DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSHTMLDocument) DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSHTMLDocument)

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

@ -478,6 +478,7 @@ DOMCI_CLASS(PaintRequestList)
DOMCI_CLASS(ScrollAreaEvent) DOMCI_CLASS(ScrollAreaEvent)
DOMCI_CLASS(PopStateEvent) DOMCI_CLASS(PopStateEvent)
DOMCI_CLASS(HashChangeEvent)
DOMCI_CLASS(EventListenerInfo) DOMCI_CLASS(EventListenerInfo)

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

@ -119,6 +119,7 @@
#include "nsIDOMMessageEvent.h" #include "nsIDOMMessageEvent.h"
#include "nsIDOMPopupBlockedEvent.h" #include "nsIDOMPopupBlockedEvent.h"
#include "nsIDOMPopStateEvent.h" #include "nsIDOMPopStateEvent.h"
#include "nsIDOMHashChangeEvent.h"
#include "nsIDOMOfflineResourceList.h" #include "nsIDOMOfflineResourceList.h"
#include "nsIDOMGeoGeolocation.h" #include "nsIDOMGeoGeolocation.h"
#include "nsIDOMDesktopNotification.h" #include "nsIDOMDesktopNotification.h"
@ -7681,19 +7682,59 @@ nsGlobalWindow::PageHidden()
mNeedsFocus = PR_TRUE; mNeedsFocus = PR_TRUE;
} }
nsresult class HashchangeCallback : public nsRunnable
nsGlobalWindow::DispatchAsyncHashchange()
{ {
FORWARD_TO_INNER(DispatchAsyncHashchange, (), NS_OK); public:
HashchangeCallback(const nsAString &aOldURL,
const nsAString &aNewURL,
nsGlobalWindow* aWindow)
: mWindow(aWindow)
{
mOldURL.Assign(aOldURL);
mNewURL.Assign(aNewURL);
}
nsCOMPtr<nsIRunnable> event = NS_IMETHOD Run()
NS_NewRunnableMethod(this, &nsGlobalWindow::FireHashchange); {
NS_PRECONDITION(NS_IsMainThread(), "Should be called on the main thread.");
return mWindow->FireHashchange(mOldURL, mNewURL);
}
return NS_DispatchToCurrentThread(event); private:
nsString mOldURL;
nsString mNewURL;
nsRefPtr<nsGlobalWindow> mWindow;
};
nsresult
nsGlobalWindow::DispatchAsyncHashchange(nsIURI *aOldURI, nsIURI *aNewURI)
{
FORWARD_TO_INNER(DispatchAsyncHashchange, (aOldURI, aNewURI), NS_OK);
// Make sure that aOldURI and aNewURI are identical up to the '#', and that
// their hashes are different.
nsCAutoString oldBeforeHash, oldHash, newBeforeHash, newHash;
nsContentUtils::SplitURIAtHash(aOldURI, oldBeforeHash, oldHash);
nsContentUtils::SplitURIAtHash(aNewURI, newBeforeHash, newHash);
NS_ENSURE_STATE(oldBeforeHash.Equals(newBeforeHash));
NS_ENSURE_STATE(!oldHash.Equals(newHash));
nsCAutoString oldSpec, newSpec;
aOldURI->GetSpec(oldSpec);
aNewURI->GetSpec(newSpec);
NS_ConvertUTF8toUTF16 oldWideSpec(oldSpec);
NS_ConvertUTF8toUTF16 newWideSpec(newSpec);
nsCOMPtr<nsIRunnable> callback =
new HashchangeCallback(oldWideSpec, newWideSpec, this);
return NS_DispatchToMainThread(callback);
} }
nsresult nsresult
nsGlobalWindow::FireHashchange() nsGlobalWindow::FireHashchange(const nsAString &aOldURL,
const nsAString &aNewURL)
{ {
NS_ENSURE_TRUE(IsInnerWindow(), NS_ERROR_FAILURE); NS_ENSURE_TRUE(IsInnerWindow(), NS_ERROR_FAILURE);
@ -7701,11 +7742,40 @@ nsGlobalWindow::FireHashchange()
if (IsFrozen()) if (IsFrozen())
return NS_OK; return NS_OK;
// Dispatch the hashchange event, which doesn't bubble and isn't cancelable, // Get a presentation shell for use in creating the hashchange event.
// to the outer window. NS_ENSURE_STATE(mDoc);
return nsContentUtils::DispatchTrustedEvent(mDoc, GetOuterWindow(),
NS_LITERAL_STRING("hashchange"), nsIPresShell *shell = mDoc->GetShell();
PR_FALSE, PR_FALSE); nsRefPtr<nsPresContext> presContext;
if (shell) {
presContext = shell->GetPresContext();
}
// Create a new hashchange event.
nsCOMPtr<nsIDOMEvent> domEvent;
nsresult rv =
nsEventDispatcher::CreateEvent(presContext, nsnull,
NS_LITERAL_STRING("hashchangeevent"),
getter_AddRefs(domEvent));
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIPrivateDOMEvent> privateEvent = do_QueryInterface(domEvent);
NS_ENSURE_TRUE(privateEvent, NS_ERROR_UNEXPECTED);
nsCOMPtr<nsIDOMHashChangeEvent> hashchangeEvent = do_QueryInterface(domEvent);
NS_ENSURE_TRUE(hashchangeEvent, NS_ERROR_UNEXPECTED);
// The hashchange event bubbles and isn't cancellable.
rv = hashchangeEvent->InitHashChangeEvent(NS_LITERAL_STRING("hashchange"),
PR_TRUE, PR_FALSE,
aOldURL, aNewURL);
NS_ENSURE_SUCCESS(rv, rv);
rv = privateEvent->SetTrusted(PR_TRUE);
NS_ENSURE_SUCCESS(rv, rv);
PRBool dummy;
return DispatchEvent(hashchangeEvent, &dummy);
} }
nsresult nsresult

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

@ -548,7 +548,7 @@ public:
virtual PRBool TakeFocus(PRBool aFocus, PRUint32 aFocusMethod); virtual PRBool TakeFocus(PRBool aFocus, PRUint32 aFocusMethod);
virtual void SetReadyForFocus(); virtual void SetReadyForFocus();
virtual void PageHidden(); virtual void PageHidden();
virtual nsresult DispatchAsyncHashchange(); virtual nsresult DispatchAsyncHashchange(nsIURI *aOldURI, nsIURI *aNewURI);
virtual nsresult DispatchSyncPopState(); virtual nsresult DispatchSyncPopState();
virtual nsresult SetArguments(nsIArray *aArguments, nsIPrincipal *aOrigin); virtual nsresult SetArguments(nsIArray *aArguments, nsIPrincipal *aOrigin);
@ -588,6 +588,8 @@ private:
void DisableAccelerationUpdates(); void DisableAccelerationUpdates();
protected: protected:
friend class HashchangeCallback;
// Object Management // Object Management
virtual ~nsGlobalWindow(); virtual ~nsGlobalWindow();
void CleanUp(PRBool aIgnoreModalDialog); void CleanUp(PRBool aIgnoreModalDialog);
@ -716,7 +718,7 @@ protected:
const nsAString &aPopupWindowName, const nsAString &aPopupWindowName,
const nsAString &aPopupWindowFeatures); const nsAString &aPopupWindowFeatures);
void FireOfflineStatusEvent(); void FireOfflineStatusEvent();
nsresult FireHashchange(); nsresult FireHashchange(const nsAString &aOldURL, const nsAString &aNewURL);
void FlushPendingNotifications(mozFlushType aType); void FlushPendingNotifications(mozFlushType aType);
void EnsureReflowFlushAndPaint(); void EnsureReflowFlushAndPaint();

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

@ -49,6 +49,7 @@
#include "nsIDOMDocument.h" #include "nsIDOMDocument.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsEvent.h" #include "nsEvent.h"
#include "nsIURI.h"
#define DOM_WINDOW_DESTROYED_TOPIC "dom-window-destroyed" #define DOM_WINDOW_DESTROYED_TOPIC "dom-window-destroyed"
@ -537,7 +538,8 @@ public:
* Instructs this window to asynchronously dispatch a hashchange event. This * Instructs this window to asynchronously dispatch a hashchange event. This
* method must be called on an inner window. * method must be called on an inner window.
*/ */
virtual nsresult DispatchAsyncHashchange() = 0; virtual nsresult DispatchAsyncHashchange(nsIURI *aOldURI,
nsIURI *aNewURI) = 0;
/** /**
* Instructs this window to synchronously dispatch a popState event. * Instructs this window to synchronously dispatch a popState event.

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

@ -88,6 +88,7 @@ XPIDLSRCS = \
nsIDOMPopStateEvent.idl \ nsIDOMPopStateEvent.idl \
nsIDOMCloseEvent.idl \ nsIDOMCloseEvent.idl \
nsIDOMEventException.idl \ nsIDOMEventException.idl \
nsIDOMHashChangeEvent.idl \
$(NULL) $(NULL)
ifdef MOZ_CSS_ANIMATIONS ifdef MOZ_CSS_ANIMATIONS

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

@ -0,0 +1,48 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is the Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsIDOMEvent.idl"
[scriptable, uuid(34850f11-8b88-43ce-8d55-9aa8b18753bd)]
interface nsIDOMHashChangeEvent : nsIDOMEvent
{
readonly attribute DOMString oldURL;
readonly attribute DOMString newURL;
void initHashChangeEvent(in DOMString typeArg,
in boolean canBubbleArg,
in boolean cancelableArg,
in DOMString oldURLArg,
in DOMString newURLArg);
};

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

@ -50,6 +50,9 @@ _TEST_FILES = \
historyframes.html \ historyframes.html \
test_497898.html \ test_497898.html \
test_bug504220.html \ test_bug504220.html \
test_bug628069_1.html \
test_bug628069_2.html \
file_bug628069.html \
test_bug631440.html \ test_bug631440.html \
test_consoleAPI.html \ test_consoleAPI.html \
test_domWindowUtils.html \ test_domWindowUtils.html \

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

@ -0,0 +1,16 @@
<html>
<body onhashchange='hashchange(event)' onload='load()'>
<script>
function hashchange(e) {
(opener || parent).childHashchange(e);
}
function load() {
(opener || parent).childLoad();
}
</script>
Just a shell of a page.
</body>
</html>

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

@ -0,0 +1,50 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=628069
-->
<head>
<title>Test for Bug 628069</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.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=628069">Mozilla Bug 628069</a>
<p id="display"></p>
<div id="content">
<iframe id="frame" style="height:100px; width:100px; border:0"></iframe>
<div id="status" style="display: none"></div>
</div>
<pre id="test">
<script type="application/javascript;version=1.7">
/** Test for Bug 628069 **/
SimpleTest.waitForExplicitFinish();
popup = window.open('file_bug628069.html');
// Control flows into childLoad, once the popup loads.
gOrigURL = null;
function childLoad() {
gOrigURL = popup.location + '';
popup.location.hash = '#hash';
// This should trigger a hashchange, so control should flow down to
// childHashchange.
}
function childHashchange(e) {
is(e.oldURL, gOrigURL, 'event.oldURL');
is(e.newURL, gOrigURL + '#hash', 'event.newURL');
popup.close();
SimpleTest.finish();
}
</script>
</body>
</html>

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

@ -0,0 +1,39 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=628069
-->
<head>
<title>Test for Bug 628069</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/EventUtils.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=628069">Mozilla Bug 628069</a>
<p id="display"></p>
<div id="content">
<iframe id="frame" style="height:100px; width:100px; border:0"></iframe>
<div id="status" style="display: none"></div>
</div>
<pre id="test">
<script type="application/javascript;version=1.7">
/** Test for Bug 628069 **/
gotHashChange = 0;
document.addEventListener("hashChange", function(e) {
gotHashChange = 1;
is(e.oldURL, "oldURL");
is(e.newURL, "newURL");
}, true);
let hc = document.createEvent("HashChangeEvent");
hc.initHashChangeEvent("hashChange", true, false, "oldURL", "newURL");
document.documentElement.dispatchEvent(hc);
is(gotHashChange, 1, 'Document received hashchange event.');
</script>
</body>
</html>

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

@ -15,7 +15,7 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=600570
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=600570">Mozilla Bug 600570</a> <a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=600570">Mozilla Bug 600570</a>
<p id="display"></p> <p id="display"></p>
<div id="content"> <div id="content">
<textarea> <textarea spellcheck="false">
aaa aaa
[bbb]</textarea> [bbb]</textarea>
</div> </div>
@ -45,17 +45,19 @@ SimpleTest.waitForFocus(function() {
t.addEventListener("input", function() { t.addEventListener("input", function() {
t.removeEventListener("input", arguments.callee, false); t.removeEventListener("input", arguments.callee, false);
is(t.value, "[aaa\nbbb]", "The value of the textarea should be correct"); setTimeout(function() { // Avoid the assertion in bug 649797
synthesizeKey("A", {accelKey: true}); is(t.value, "[aaa\nbbb]", "The value of the textarea should be correct");
is(t.selectionStart, 0, "Select all should set the selection start to the beginning of textarea"); synthesizeKey("A", {accelKey: true});
is(t.selectionEnd, 9, "Select all should set the selection end to the end of textarea"); is(t.selectionStart, 0, "Select all should set the selection start to the beginning of textarea");
is(t.selectionEnd, 9, "Select all should set the selection end to the end of textarea");
var afterPaste = snapshotWindow(window); var afterPaste = snapshotWindow(window);
var res = compareSnapshots(afterSetValue, afterPaste, true); var res = compareSnapshots(afterSetValue, afterPaste, true);
ok(res[0], "Pasting and setting the value directly should result in the same rendering"); ok(res[0], "Pasting and setting the value directly should result in the same rendering");
SimpleTest.finish(); SimpleTest.finish();
}, 0);
}, false); }, false);
synthesizeKey("VK_RIGHT", {}); synthesizeKey("VK_RIGHT", {});
synthesizeKey("V", {accelKey: true}); synthesizeKey("V", {accelKey: true});

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

@ -80,7 +80,7 @@ typedef struct _cairo_d2d_device cairo_d2d_device_t;
struct _cairo_d2d_surface { struct _cairo_d2d_surface {
_cairo_d2d_surface() : d2d_clip(NULL), clipping(false), isDrawing(false), _cairo_d2d_surface() : d2d_clip(NULL), clipping(false), isDrawing(false),
textRenderingInit(true) textRenderingInit(false)
{ {
_cairo_clip_init (&this->clip); _cairo_clip_init (&this->clip);
} }

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

@ -176,7 +176,7 @@ cairo_d2d_create_device_from_d3d10device(ID3D10Device1 *d3d10device)
CD3D10_BUFFER_DESC bufferDesc(sizeof(vertices), D3D10_BIND_VERTEX_BUFFER); CD3D10_BUFFER_DESC bufferDesc(sizeof(vertices), D3D10_BIND_VERTEX_BUFFER);
D3D10_SUBRESOURCE_DATA data; D3D10_SUBRESOURCE_DATA data;
CD3D10_TEXTURE2D_DESC textDesc(DXGI_FORMAT_B8G8R8A8_UNORM, CD3D10_TEXTURE2D_DESC textDesc(DXGI_FORMAT_B8G8R8A8_UNORM,
TEXT_TEXTURE_WIDTH, TEXT_TEXTURE_WIDTH,
TEXT_TEXTURE_HEIGHT, TEXT_TEXTURE_HEIGHT,
1, 1); 1, 1);
@ -352,10 +352,10 @@ cairo_release_device(cairo_device_t *device)
if (!newrefcnt) { if (!newrefcnt) {
// Call the correct destructor // Call the correct destructor
cairo_d2d_device_t *d2d_device = reinterpret_cast<cairo_d2d_device_t*>(device); cairo_d2d_device_t *d2d_device = reinterpret_cast<cairo_d2d_device_t*>(device);
HMODULE d3d10_1 = d2d_device->mD3D10_1; HMODULE d3d10_1 = d2d_device->mD3D10_1;
delete d2d_device; delete d2d_device;
_cairo_d2d_release_factory(); _cairo_d2d_release_factory();
FreeLibrary(d3d10_1); FreeLibrary(d3d10_1);
} }
return newrefcnt; return newrefcnt;
} }
@ -779,7 +779,7 @@ _cairo_d2d_get_buffer_texture(cairo_d2d_surface_t *surface)
surface->surface->QueryInterface(&surf); surface->surface->QueryInterface(&surf);
surf->GetDesc(&surfDesc); surf->GetDesc(&surfDesc);
CD3D10_TEXTURE2D_DESC softDesc(surfDesc.Format, surfDesc.Width, surfDesc.Height); CD3D10_TEXTURE2D_DESC softDesc(surfDesc.Format, surfDesc.Width, surfDesc.Height);
softDesc.MipLevels = 1; softDesc.MipLevels = 1;
softDesc.Usage = D3D10_USAGE_DEFAULT; softDesc.Usage = D3D10_USAGE_DEFAULT;
softDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE; softDesc.BindFlags = D3D10_BIND_RENDER_TARGET | D3D10_BIND_SHADER_RESOURCE;
surface->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &surface->bufferTexture); surface->device->mD3D10Device->CreateTexture2D(&softDesc, NULL, &surface->bufferTexture);
@ -1158,9 +1158,9 @@ _cairo_d2d_create_strokestyle_for_stroke_style(const cairo_stroke_style_t *style
(FLOAT)style->miter_limit, (FLOAT)style->miter_limit,
dashStyle, dashStyle,
(FLOAT)style->dash_offset), (FLOAT)style->dash_offset),
dashes, dashes,
style->num_dashes, style->num_dashes,
&strokeStyle); &strokeStyle);
delete [] dashes; delete [] dashes;
return strokeStyle; return strokeStyle;
} }
@ -1225,7 +1225,7 @@ static void _d2d_snapshot_detached(cairo_surface_t *surface)
} }
if (!--existingBitmap->refs) { if (!--existingBitmap->refs) {
cache_usage -= _d2d_compute_bitmap_mem_size(existingBitmap->bitmap); cache_usage -= _d2d_compute_bitmap_mem_size(existingBitmap->bitmap);
delete existingBitmap; delete existingBitmap;
} }
cairo_surface_destroy(surface); cairo_surface_destroy(surface);
} }
@ -1463,8 +1463,8 @@ _cairo_d2d_create_radial_gradient_brush(cairo_d2d_surface_t *d2dsurf,
} else if (source_pattern->base.base.extend == CAIRO_EXTEND_NONE) { } else if (source_pattern->base.base.extend == CAIRO_EXTEND_NONE) {
float offset_factor = (outer_radius - inner_radius) / outer_radius; float offset_factor = (outer_radius - inner_radius) / outer_radius;
float global_offset = inner_radius / outer_radius; float global_offset = inner_radius / outer_radius;
num_stops++; // Add a stop on the outer radius. num_stops++; // Add a stop on the outer radius.
if (inner_radius != 0) { if (inner_radius != 0) {
num_stops++; // Add a stop on the inner radius. num_stops++; // Add a stop on the inner radius.
} }
@ -1572,11 +1572,11 @@ _cairo_d2d_create_linear_gradient_brush(cairo_d2d_surface_t *d2dsurf,
double max_dist, min_dist; double max_dist, min_dist;
max_dist = MAX(_cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_left, p1)), max_dist = MAX(_cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_left, p1)),
_cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_right, p1))); _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_right, p1)));
max_dist = MAX(max_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_left, p1))); max_dist = MAX(max_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_left, p1)));
max_dist = MAX(max_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_right, p1))); max_dist = MAX(max_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_right, p1)));
min_dist = MIN(_cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_left, p1)), min_dist = MIN(_cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_left, p1)),
_cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_right, p1))); _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(top_right, p1)));
min_dist = MIN(min_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_left, p1))); min_dist = MIN(min_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_left, p1)));
min_dist = MIN(min_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_right, p1))); min_dist = MIN(min_dist, _cairo_d2d_dot_product(u, _cairo_d2d_subtract_point(bottom_right, p1)));
@ -1758,7 +1758,7 @@ _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf,
*/ */
return NULL; return NULL;
} }
if (srcSurf->device != d2dsurf->device) { if (srcSurf->device != d2dsurf->device) {
/* This code does not work if the source surface does not use /* This code does not work if the source surface does not use
* the same device. Some work could be done to do something * the same device. Some work could be done to do something
* fairly efficient here, for now, fallback. * fairly efficient here, for now, fallback.
@ -1830,7 +1830,7 @@ _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf,
/* First we check which part of the image is inside the viewable area. */ /* First we check which part of the image is inside the viewable area. */
_cairo_d2d_calculate_visible_rect(d2dsurf, srcSurf, &mat, &xoffset, &yoffset, &width, &height); _cairo_d2d_calculate_visible_rect(d2dsurf, srcSurf, &mat, &xoffset, &yoffset, &width, &height);
cairo_matrix_translate(&mat, xoffset, yoffset); cairo_matrix_translate(&mat, xoffset, yoffset);
if (width > maxSize || height > maxSize) { if (width > maxSize || height > maxSize) {
/* /*
@ -1840,11 +1840,11 @@ _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf,
* We need to size it to at least the diagonal size of this surface, in order to prevent ever * We need to size it to at least the diagonal size of this surface, in order to prevent ever
* upsampling this again when drawing it to the surface. We want the resized surface * upsampling this again when drawing it to the surface. We want the resized surface
* to be as small as possible to limit pixman required fill rate. * to be as small as possible to limit pixman required fill rate.
* *
* Note this isn't necessarily perfect. Imagine having a 5x5 pixel destination and * Note this isn't necessarily perfect. Imagine having a 5x5 pixel destination and
* a 10x5 image containing a line of blackpixels, white pixels, black pixels, if you rotate * a 10x5 image containing a line of blackpixels, white pixels, black pixels, if you rotate
* this by 45 degrees and scale it to a size of 5x5 pixels and composite it to the destination, * this by 45 degrees and scale it to a size of 5x5 pixels and composite it to the destination,
* the composition will require all 10 original columns to do the best possible sampling. * the composition will require all 10 original columns to do the best possible sampling.
*/ */
RefPtr<IDXGISurface> surf; RefPtr<IDXGISurface> surf;
d2dsurf->surface->QueryInterface(&surf); d2dsurf->surface->QueryInterface(&surf);
@ -1978,10 +1978,10 @@ _cairo_d2d_create_brush_for_pattern(cairo_d2d_surface_t *d2dsurf,
cachebitmap->dirty = false; cachebitmap->dirty = false;
cachebitmap->bitmap = sourceBitmap; cachebitmap->bitmap = sourceBitmap;
cachebitmap->device = d2dsurf->device; cachebitmap->device = d2dsurf->device;
/* /*
* This will start out with two references, one on the snapshot * This will start out with two references, one on the snapshot
* and one more in the user data structure. * and one more in the user data structure.
*/ */
cachebitmap->refs = 2; cachebitmap->refs = 2;
cairo_surface_set_user_data(surfacePattern->surface, cairo_surface_set_user_data(surfacePattern->surface,
key, key,
@ -2272,7 +2272,7 @@ _cairo_d2d_clear (cairo_d2d_surface_t *d2dsurf,
d2dsurf->rt->PushAxisAlignedClip( d2dsurf->rt->PushAxisAlignedClip(
D2D1::RectF((FLOAT)rect.x, D2D1::RectF((FLOAT)rect.x,
(FLOAT)rect.y, (FLOAT)rect.y,
(FLOAT)rect.x + rect.width, (FLOAT)rect.x + rect.width,
(FLOAT)rect.y + rect.height), (FLOAT)rect.y + rect.height),
D2D1_ANTIALIAS_MODE_ALIASED); D2D1_ANTIALIAS_MODE_ALIASED);
@ -2288,7 +2288,7 @@ _cairo_d2d_clear (cairo_d2d_surface_t *d2dsurf,
} }
static cairo_operator_t _cairo_d2d_simplify_operator(cairo_operator_t op, static cairo_operator_t _cairo_d2d_simplify_operator(cairo_operator_t op,
const cairo_pattern_t *source) const cairo_pattern_t *source)
{ {
if (op == CAIRO_OPERATOR_SOURCE) { if (op == CAIRO_OPERATOR_SOURCE) {
/** Operator over is easier for D2D! If the source if opaque, change */ /** Operator over is easier for D2D! If the source if opaque, change */
@ -2655,7 +2655,7 @@ _cairo_d2d_copy_surface(cairo_d2d_surface_t *dst,
src->device->mD3D10Device->CopyResource(srcResource, src->surface); src->device->mD3D10Device->CopyResource(srcResource, src->surface);
} else { } else {
// Need to flush the source too if it's a different surface. // Need to flush the source too if it's a different surface.
_cairo_d2d_flush(src); _cairo_d2d_flush(src);
} }
// One copy for each rectangle in the final clipping region. // One copy for each rectangle in the final clipping region.
@ -2705,7 +2705,7 @@ _cairo_d2d_blend_surface(cairo_d2d_surface_t *dst,
const cairo_matrix_t *transform, const cairo_matrix_t *transform,
cairo_box_t *box, cairo_box_t *box,
cairo_clip_t *clip, cairo_clip_t *clip,
cairo_filter_t filter, cairo_filter_t filter,
float opacity) float opacity)
{ {
if (dst == src) { if (dst == src) {
@ -2804,12 +2804,12 @@ _cairo_d2d_blend_surface(cairo_d2d_surface_t *dst,
*/ */
static cairo_int_status_t static cairo_int_status_t
_cairo_d2d_try_fastblit(cairo_d2d_surface_t *dst, _cairo_d2d_try_fastblit(cairo_d2d_surface_t *dst,
cairo_surface_t *src, cairo_surface_t *src,
cairo_box_t *box, cairo_box_t *box,
const cairo_matrix_t *matrix, const cairo_matrix_t *matrix,
cairo_clip_t *clip, cairo_clip_t *clip,
cairo_operator_t op, cairo_operator_t op,
cairo_filter_t filter, cairo_filter_t filter,
float opacity = 1.0f) float opacity = 1.0f)
{ {
if (op == CAIRO_OPERATOR_OVER && src->content == CAIRO_CONTENT_COLOR) { if (op == CAIRO_OPERATOR_OVER && src->content == CAIRO_CONTENT_COLOR) {
@ -3102,8 +3102,8 @@ _cairo_d2d_paint(void *surface,
reinterpret_cast<const cairo_surface_pattern_t*>(source); reinterpret_cast<const cairo_surface_pattern_t*>(source);
status = _cairo_d2d_try_fastblit(d2dsurf, surf_pattern->surface, status = _cairo_d2d_try_fastblit(d2dsurf, surf_pattern->surface,
NULL, &source->matrix, clip, NULL, &source->matrix, clip,
op, source->filter); op, source->filter);
if (status != CAIRO_INT_STATUS_UNSUPPORTED) { if (status != CAIRO_INT_STATUS_UNSUPPORTED) {
return status; return status;
@ -3230,7 +3230,7 @@ _cairo_d2d_mask(void *surface,
&source->matrix, &source->matrix,
clip, clip,
op, op,
source->filter, source->filter,
solidAlphaValue); solidAlphaValue);
if (rv != CAIRO_INT_STATUS_UNSUPPORTED) { if (rv != CAIRO_INT_STATUS_UNSUPPORTED) {
return rv; return rv;
@ -3432,8 +3432,8 @@ _cairo_d2d_fill(void *surface,
const cairo_surface_pattern_t *surf_pattern = const cairo_surface_pattern_t *surf_pattern =
reinterpret_cast<const cairo_surface_pattern_t*>(source); reinterpret_cast<const cairo_surface_pattern_t*>(source);
cairo_int_status_t rv = _cairo_d2d_try_fastblit(d2dsurf, surf_pattern->surface, cairo_int_status_t rv = _cairo_d2d_try_fastblit(d2dsurf, surf_pattern->surface,
&box, &source->matrix, clip, op, &box, &source->matrix, clip, op,
source->filter); source->filter);
if (rv != CAIRO_INT_STATUS_UNSUPPORTED) { if (rv != CAIRO_INT_STATUS_UNSUPPORTED) {
return rv; return rv;
@ -3583,7 +3583,11 @@ _cairo_dwrite_manual_show_glyphs_on_d2d_surface(void *surface,
} }
// Deal with rendering modes CreateGlyphRunAnalysis doesn't accept. // Deal with rendering modes CreateGlyphRunAnalysis doesn't accept.
if (renderMode == DWRITE_RENDERING_MODE_DEFAULT) { switch (renderMode) {
case DWRITE_RENDERING_MODE_ALIASED:
// ClearType texture creation will fail in this mode, so bail out
return CAIRO_INT_STATUS_UNSUPPORTED;
case DWRITE_RENDERING_MODE_DEFAULT:
// As per DWRITE_RENDERING_MODE documentation, pick Natural for font // As per DWRITE_RENDERING_MODE documentation, pick Natural for font
// sizes under 16 ppem // sizes under 16 ppem
if (scaled_font->base.font_matrix.yy < 16.0f) { if (scaled_font->base.font_matrix.yy < 16.0f) {
@ -3591,15 +3595,24 @@ _cairo_dwrite_manual_show_glyphs_on_d2d_surface(void *surface,
} else { } else {
renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
} }
} else if (renderMode == DWRITE_RENDERING_MODE_OUTLINE) { break;
case DWRITE_RENDERING_MODE_OUTLINE:
renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC; renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
break;
default:
break;
} }
DWRITE_MEASURING_MODE measureMode =
renderMode <= DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC ? DWRITE_MEASURING_MODE_GDI_CLASSIC :
renderMode == DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL ? DWRITE_MEASURING_MODE_GDI_NATURAL :
DWRITE_MEASURING_MODE_NATURAL;
hr = DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, hr = DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run,
1.0f, 1.0f,
transform ? &dwmat : 0, transform ? &dwmat : 0,
renderMode, renderMode,
DWRITE_MEASURING_MODE_NATURAL, measureMode,
0, 0,
0, 0,
&analysis); &analysis);
@ -3799,7 +3812,7 @@ _cairo_dwrite_manual_show_glyphs_on_d2d_surface(void *surface,
ID3D10EffectVectorVariable *textColor = effect->GetVariableByName("TextColor")->AsVector(); ID3D10EffectVectorVariable *textColor = effect->GetVariableByName("TextColor")->AsVector();
float colorVal[] = { float(source->color.red * source->color.alpha), float colorVal[] = { float(source->color.red * source->color.alpha),
float(source->color.green * source->color.alpha), float(source->color.green * source->color.alpha),
float(source->color.blue * source->color.alpha), float(source->color.blue * source->color.alpha),
float(source->color.alpha) }; float(source->color.alpha) };
textColor->SetFloatVector(colorVal); textColor->SetFloatVector(colorVal);
@ -3919,18 +3932,62 @@ _cairo_dwrite_show_glyphs_on_d2d_surface(void *surface,
cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE; cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
} }
switch (dwritesf->antialias_mode) { RefPtr<IDWriteRenderingParams> params;
case CAIRO_ANTIALIAS_NONE: target_rt->GetTextRenderingParams(&params);
target_rt->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_ALIASED);
break; DWRITE_RENDERING_MODE renderMode = DWRITE_RENDERING_MODE_DEFAULT;
case CAIRO_ANTIALIAS_GRAY: if (params) {
target_rt->SetTextAntialiasMode(D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE); HRESULT hr = dwriteff->dwriteface->GetRecommendedRenderingMode(
break; (FLOAT)dwritesf->base.font_matrix.yy,
case CAIRO_ANTIALIAS_SUBPIXEL: 1.0f,
target_rt->SetTextAntialiasMode(cleartype_quality); DWRITE_MEASURING_MODE_NATURAL,
break; params,
&renderMode);
if (FAILED(hr)) {
// this probably never happens, but let's play it safe
renderMode = DWRITE_RENDERING_MODE_DEFAULT;
}
} }
// Deal with rendering modes CreateGlyphRunAnalysis doesn't accept
switch (renderMode) {
case DWRITE_RENDERING_MODE_DEFAULT:
// As per DWRITE_RENDERING_MODE documentation, pick Natural for font
// sizes under 16 ppem
if (dwritesf->base.font_matrix.yy < 16.0f) {
renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL;
} else {
renderMode = DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC;
}
break;
case DWRITE_RENDERING_MODE_OUTLINE:
return CAIRO_INT_STATUS_UNSUPPORTED;
default:
break;
}
switch (dwritesf->antialias_mode) {
case CAIRO_ANTIALIAS_NONE:
cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
break;
case CAIRO_ANTIALIAS_GRAY:
cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_GRAYSCALE;
break;
case CAIRO_ANTIALIAS_SUBPIXEL:
break;
}
if (renderMode == DWRITE_RENDERING_MODE_ALIASED) {
cleartype_quality = D2D1_TEXT_ANTIALIAS_MODE_ALIASED;
}
target_rt->SetTextAntialiasMode(cleartype_quality);
DWRITE_MEASURING_MODE measureMode =
renderMode <= DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC ? DWRITE_MEASURING_MODE_GDI_CLASSIC :
renderMode == DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL ? DWRITE_MEASURING_MODE_GDI_NATURAL :
DWRITE_MEASURING_MODE_NATURAL;
cairo_bool_t transform = FALSE; cairo_bool_t transform = FALSE;
DWRITE_GLYPH_RUN run; DWRITE_GLYPH_RUN run;
@ -3949,8 +4006,8 @@ _cairo_dwrite_show_glyphs_on_d2d_surface(void *surface,
DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run, DWriteFactory::Instance()->CreateGlyphRunAnalysis(&run,
1.0f, 1.0f,
transform ? &dwmat : 0, transform ? &dwmat : 0,
DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC, renderMode,
DWRITE_MEASURING_MODE_NATURAL, measureMode,
0, 0,
0, 0,
&analysis); &analysis);
@ -3987,7 +4044,7 @@ _cairo_dwrite_show_glyphs_on_d2d_surface(void *surface,
brush->SetTransform(&mat_brush); brush->SetTransform(&mat_brush);
} }
target_rt->DrawGlyphRun(D2D1::Point2F(0, 0), &run, brush, dwritesf->measuring_mode); target_rt->DrawGlyphRun(D2D1::Point2F(0, 0), &run, brush, measureMode);
if (transform) { if (transform) {
target_rt->SetTransform(D2D1::Matrix3x2F::Identity()); target_rt->SetTransform(D2D1::Matrix3x2F::Identity());
@ -4019,14 +4076,13 @@ _cairo_d2d_show_glyphs (void *surface,
} }
cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(surface); cairo_d2d_surface_t *d2dsurf = static_cast<cairo_d2d_surface_t*>(surface);
if (!d2dsurf->textRenderingInit) { if (!d2dsurf->textRenderingInit) {
RefPtr<IDWriteRenderingParams> params; RefPtr<IDWriteRenderingParams> params = DWriteFactory::RenderingParams();
DWriteFactory::Instance()->CreateRenderingParams(&params);
d2dsurf->rt->SetTextRenderingParams(params); d2dsurf->rt->SetTextRenderingParams(params);
d2dsurf->textRenderingInit = true; d2dsurf->textRenderingInit = true;
} }
cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED; cairo_int_status_t status = CAIRO_INT_STATUS_UNSUPPORTED;
if (scaled_font->backend->type == CAIRO_FONT_TYPE_DWRITE) { if (scaled_font->backend->type == CAIRO_FONT_TYPE_DWRITE) {
status = (cairo_int_status_t) status = (cairo_int_status_t)
_cairo_dwrite_show_glyphs_on_d2d_surface(surface, op, source, glyphs, num_glyphs, scaled_font, clip); _cairo_dwrite_show_glyphs_on_d2d_surface(surface, op, source, glyphs, num_glyphs, scaled_font, clip);
} }
@ -4131,7 +4187,7 @@ cairo_d2d_surface_create_for_hwnd(cairo_device_t *cairo_device,
} }
/** Get the backbuffer surface from the swap chain */ /** Get the backbuffer surface from the swap chain */
hr = newSurf->dxgiChain->GetBuffer(0, hr = newSurf->dxgiChain->GetBuffer(0,
IID_PPV_ARGS(&newSurf->surface)); IID_PPV_ARGS(&newSurf->surface));
if (FAILED(hr)) { if (FAILED(hr)) {
goto FAIL_HWND; goto FAIL_HWND;
@ -4176,8 +4232,8 @@ FAIL_HWND:
cairo_surface_t * cairo_surface_t *
cairo_d2d_surface_create(cairo_device_t *device, cairo_d2d_surface_create(cairo_device_t *device,
cairo_format_t format, cairo_format_t format,
int width, int width,
int height) int height)
{ {
if (width == 0 || height == 0) { if (width == 0 || height == 0) {
return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_SIZE)); return _cairo_surface_create_in_error(_cairo_error(CAIRO_STATUS_INVALID_SIZE));
@ -4474,7 +4530,7 @@ FAIL_CREATE:
void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t *clip) void cairo_d2d_scroll(cairo_surface_t *surface, int x, int y, cairo_rectangle_t *clip)
{ {
if (surface->type != CAIRO_SURFACE_TYPE_D2D) { if (surface->type != CAIRO_SURFACE_TYPE_D2D) {
return; return;
} }
cairo_d2d_surface_t *d2dsurf = reinterpret_cast<cairo_d2d_surface_t*>(surface); cairo_d2d_surface_t *d2dsurf = reinterpret_cast<cairo_d2d_surface_t*>(surface);
@ -4537,7 +4593,7 @@ HDC
cairo_d2d_get_dc(cairo_surface_t *surface, cairo_bool_t retain_contents) cairo_d2d_get_dc(cairo_surface_t *surface, cairo_bool_t retain_contents)
{ {
if (surface->type != CAIRO_SURFACE_TYPE_D2D) { if (surface->type != CAIRO_SURFACE_TYPE_D2D) {
return NULL; return NULL;
} }
cairo_d2d_surface_t *d2dsurf = reinterpret_cast<cairo_d2d_surface_t*>(surface); cairo_d2d_surface_t *d2dsurf = reinterpret_cast<cairo_d2d_surface_t*>(surface);
@ -4578,7 +4634,7 @@ void
cairo_d2d_release_dc(cairo_surface_t *surface, const cairo_rectangle_int_t *updated_rect) cairo_d2d_release_dc(cairo_surface_t *surface, const cairo_rectangle_int_t *updated_rect)
{ {
if (surface->type != CAIRO_SURFACE_TYPE_D2D) { if (surface->type != CAIRO_SURFACE_TYPE_D2D) {
return; return;
} }
cairo_d2d_surface_t *d2dsurf = reinterpret_cast<cairo_d2d_surface_t*>(surface); cairo_d2d_surface_t *d2dsurf = reinterpret_cast<cairo_d2d_surface_t*>(surface);

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

@ -119,6 +119,13 @@ private:
IDWriteFactory *DWriteFactory::mFactoryInstance = NULL; IDWriteFactory *DWriteFactory::mFactoryInstance = NULL;
IDWriteFontCollection *DWriteFactory::mSystemCollection = NULL; IDWriteFontCollection *DWriteFactory::mSystemCollection = NULL;
IDWriteRenderingParams *DWriteFactory::mRenderingParams = NULL;
FLOAT DWriteFactory::mGamma = -1.0;
FLOAT DWriteFactory::mEnhancedContrast = -1.0;
FLOAT DWriteFactory::mClearTypeLevel = -1.0;
int DWriteFactory::mPixelGeometry = -1;
int DWriteFactory::mRenderingMode = -1;
ID2D1Factory *D2DFactory::mFactoryInstance = NULL; ID2D1Factory *D2DFactory::mFactoryInstance = NULL;
ID2D1DCRenderTarget *D2DFactory::mRenderTarget = NULL; ID2D1DCRenderTarget *D2DFactory::mRenderTarget = NULL;
@ -298,7 +305,6 @@ _cairo_dwrite_font_face_create_for_toy (cairo_toy_font_face_t *toy_face,
if (SUCCEEDED(hr)) { if (SUCCEEDED(hr)) {
// Cannot use C++ style new since cairo deallocates this. // Cannot use C++ style new since cairo deallocates this.
*font_face = (cairo_font_face_t*)face; *font_face = (cairo_font_face_t*)face;
_cairo_font_face_init (&(*(_cairo_dwrite_font_face**)font_face)->base, &_cairo_dwrite_font_face_backend); _cairo_font_face_init (&(*(_cairo_dwrite_font_face**)font_face)->base, &_cairo_dwrite_font_face_backend);
} else { } else {
free(face); free(face);
@ -349,7 +355,7 @@ _cairo_dwrite_glyph_run_from_glyphs(cairo_glyph_t *glyphs,
scaled_font->mat.xx == scaled_font->base.font_matrix.xx && scaled_font->mat.xx == scaled_font->base.font_matrix.xx &&
scaled_font->mat.yy == scaled_font->base.font_matrix.yy) { scaled_font->mat.yy == scaled_font->base.font_matrix.yy) {
// Fast route, don't actually use a transform but just // Fast route, don't actually use a transform but just
// set the correct font size. // set the correct font size.
*transformed = 0; *transformed = 0;
run->fontEmSize = (FLOAT)scaled_font->base.font_matrix.yy; run->fontEmSize = (FLOAT)scaled_font->base.font_matrix.yy;
@ -371,14 +377,14 @@ _cairo_dwrite_glyph_run_from_glyphs(cairo_glyph_t *glyphs,
cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y); cairo_matrix_transform_point(&scaled_font->mat_inverse, &x, &y);
// Since we will multiply by our ctm matrix later for rotation effects // Since we will multiply by our ctm matrix later for rotation effects
// and such, adjust positions by the inverse matrix now. Y-axis is // and such, adjust positions by the inverse matrix now. Y-axis is
// inverted! Therefor the offset is -y. // inverted! Therefor the offset is -y.
offsets[i].ascenderOffset = -(FLOAT)y; offsets[i].ascenderOffset = -(FLOAT)y;
offsets[i].advanceOffset = (FLOAT)x; offsets[i].advanceOffset = (FLOAT)x;
advances[i] = 0.0; advances[i] = 0.0;
} }
// The font matrix takes care of the scaling if we have a transform, // The font matrix takes care of the scaling if we have a transform,
// emSize should be 1. // emSize should be 1.
run->fontEmSize = 1.0f; run->fontEmSize = 1.0f;
} }
} }
@ -715,22 +721,22 @@ _cairo_dwrite_scaled_font_init_glyph_metrics(cairo_dwrite_scaled_font_t *scaled_
// TODO: Treat swap_xy. // TODO: Treat swap_xy.
extents.width = (FLOAT)(metrics.advanceWidth - metrics.leftSideBearing - metrics.rightSideBearing) / extents.width = (FLOAT)(metrics.advanceWidth - metrics.leftSideBearing - metrics.rightSideBearing) /
fontMetrics.designUnitsPerEm; fontMetrics.designUnitsPerEm;
extents.height = (FLOAT)(metrics.advanceHeight - metrics.topSideBearing - metrics.bottomSideBearing) / extents.height = (FLOAT)(metrics.advanceHeight - metrics.topSideBearing - metrics.bottomSideBearing) /
fontMetrics.designUnitsPerEm; fontMetrics.designUnitsPerEm;
extents.x_advance = (FLOAT)metrics.advanceWidth / fontMetrics.designUnitsPerEm; extents.x_advance = (FLOAT)metrics.advanceWidth / fontMetrics.designUnitsPerEm;
extents.x_bearing = (FLOAT)metrics.leftSideBearing / fontMetrics.designUnitsPerEm; extents.x_bearing = (FLOAT)metrics.leftSideBearing / fontMetrics.designUnitsPerEm;
extents.y_advance = 0.0; extents.y_advance = 0.0;
extents.y_bearing = (FLOAT)(metrics.topSideBearing - metrics.verticalOriginY) / extents.y_bearing = (FLOAT)(metrics.topSideBearing - metrics.verticalOriginY) /
fontMetrics.designUnitsPerEm; fontMetrics.designUnitsPerEm;
// We pad the extents here because GetDesignGlyphMetrics returns "ideal" metrics // We pad the extents here because GetDesignGlyphMetrics returns "ideal" metrics
// for the glyph outline, without accounting for hinting/gridfitting/antialiasing, // for the glyph outline, without accounting for hinting/gridfitting/antialiasing,
// and therefore it does not always cover all pixels that will actually be touched. // and therefore it does not always cover all pixels that will actually be touched.
if (scaled_font->base.options.antialias != CAIRO_ANTIALIAS_NONE && if (scaled_font->base.options.antialias != CAIRO_ANTIALIAS_NONE &&
extents.width > 0 && extents.height > 0) { extents.width > 0 && extents.height > 0) {
extents.width += scaled_font->mat_inverse.xx * 2; extents.width += scaled_font->mat_inverse.xx * 2;
extents.x_bearing -= scaled_font->mat_inverse.xx; extents.x_bearing -= scaled_font->mat_inverse.xx;
} }
_cairo_scaled_glyph_set_metrics (scaled_glyph, _cairo_scaled_glyph_set_metrics (scaled_glyph,
@ -753,22 +759,22 @@ public:
// IUnknown interface // IUnknown interface
IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject) IFACEMETHOD(QueryInterface)(IID const& iid, OUT void** ppObject)
{ {
if (iid != __uuidof(IDWriteGeometrySink)) if (iid != __uuidof(IDWriteGeometrySink))
return E_NOINTERFACE; return E_NOINTERFACE;
*ppObject = static_cast<IDWriteGeometrySink*>(this); *ppObject = static_cast<IDWriteGeometrySink*>(this);
return S_OK; return S_OK;
} }
IFACEMETHOD_(ULONG, AddRef)() IFACEMETHOD_(ULONG, AddRef)()
{ {
return 1; return 1;
} }
IFACEMETHOD_(ULONG, Release)() IFACEMETHOD_(ULONG, Release)()
{ {
return 1; return 1;
} }
IFACEMETHODIMP_(void) SetFillMode(D2D1_FILL_MODE fillMode) IFACEMETHODIMP_(void) SetFillMode(D2D1_FILL_MODE fillMode)
@ -822,7 +828,7 @@ public:
IFACEMETHODIMP_(void) AddBeziers( IFACEMETHODIMP_(void) AddBeziers(
const D2D1_BEZIER_SEGMENT *beziers, const D2D1_BEZIER_SEGMENT *beziers,
UINT beziersCount) UINT beziersCount)
{ {
for (unsigned int i = 0; i < beziersCount; i++) { for (unsigned int i = 0; i < beziersCount; i++) {
cairo_status_t status = _cairo_path_fixed_curve_to(mCairoPath, cairo_status_t status = _cairo_path_fixed_curve_to(mCairoPath,
@ -837,7 +843,7 @@ public:
IFACEMETHODIMP_(void) AddLines( IFACEMETHODIMP_(void) AddLines(
const D2D1_POINT_2F *points, const D2D1_POINT_2F *points,
UINT pointsCount) UINT pointsCount)
{ {
for (unsigned int i = 0; i < pointsCount; i++) { for (unsigned int i = 0; i < pointsCount; i++) {
cairo_status_t status = _cairo_path_fixed_line_to(mCairoPath, cairo_status_t status = _cairo_path_fixed_line_to(mCairoPath,
@ -853,7 +859,7 @@ private:
cairo_int_status_t cairo_int_status_t
_cairo_dwrite_scaled_font_init_glyph_path(cairo_dwrite_scaled_font_t *scaled_font, _cairo_dwrite_scaled_font_init_glyph_path(cairo_dwrite_scaled_font_t *scaled_font,
cairo_scaled_glyph_t *scaled_glyph) cairo_scaled_glyph_t *scaled_glyph)
{ {
cairo_path_fixed_t *path; cairo_path_fixed_t *path;
path = _cairo_path_fixed_create(); path = _cairo_path_fixed_create();
@ -999,8 +1005,8 @@ _cairo_dwrite_scaled_font_init_glyph_surface(cairo_dwrite_scaled_font_t *scaled_
cairo_surface_set_device_offset (image, -x1, -y1); cairo_surface_set_device_offset (image, -x1, -y1);
_cairo_scaled_glyph_set_surface (scaled_glyph, _cairo_scaled_glyph_set_surface (scaled_glyph,
&scaled_font->base, &scaled_font->base,
(cairo_image_surface_t *) image); (cairo_image_surface_t *) image);
FAIL: FAIL:
cairo_surface_destroy (&surface->base); cairo_surface_destroy (&surface->base);
@ -1009,7 +1015,7 @@ _cairo_dwrite_scaled_font_init_glyph_surface(cairo_dwrite_scaled_font_t *scaled_
} }
cairo_int_status_t cairo_int_status_t
_cairo_dwrite_load_truetype_table(void *scaled_font, _cairo_dwrite_load_truetype_table(void *scaled_font,
unsigned long tag, unsigned long tag,
long offset, long offset,
unsigned char *buffer, unsigned char *buffer,
@ -1070,6 +1076,13 @@ cairo_dwrite_scaled_font_allow_manual_show_glyphs(void* dwrite_scaled_font, cair
font->manual_show_glyphs_allowed = allowed; font->manual_show_glyphs_allowed = allowed;
} }
void
cairo_dwrite_set_cleartype_params(FLOAT gamma, FLOAT contrast, FLOAT level,
int geometry, int mode)
{
DWriteFactory::SetRenderingParams(gamma, contrast, level, geometry, mode);
}
cairo_int_status_t cairo_int_status_t
_dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface, _dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface,
DWRITE_MATRIX *transform, DWRITE_MATRIX *transform,
@ -1081,8 +1094,7 @@ _dwrite_draw_glyphs_to_gdi_surface_gdi(cairo_win32_surface_t *surface,
DWriteFactory::Instance()->GetGdiInterop(&gdiInterop); DWriteFactory::Instance()->GetGdiInterop(&gdiInterop);
IDWriteBitmapRenderTarget *rt; IDWriteBitmapRenderTarget *rt;
IDWriteRenderingParams *params; IDWriteRenderingParams *params = DWriteFactory::RenderingParams();
DWriteFactory::Instance()->CreateRenderingParams(&params);
gdiInterop->CreateBitmapRenderTarget(surface->dc, gdiInterop->CreateBitmapRenderTarget(surface->dc,
area.right - area.left, area.right - area.left,
@ -1304,7 +1316,7 @@ _cairo_dwrite_show_glyphs_on_surface(void *surface,
offsets[i].advanceOffset = (FLOAT)(glyphs[i].x - fontArea.left); offsets[i].advanceOffset = (FLOAT)(glyphs[i].x - fontArea.left);
advances[i] = 0.0; advances[i] = 0.0;
} }
run.fontEmSize = (FLOAT)scaled_font->font_matrix.yy; run.fontEmSize = (FLOAT)scaled_font->font_matrix.yy;
} else { } else {
transform = TRUE; transform = TRUE;
@ -1314,10 +1326,10 @@ _cairo_dwrite_show_glyphs_on_surface(void *surface,
double y = glyphs[i].y - fontArea.top; double y = glyphs[i].y - fontArea.top;
cairo_matrix_transform_point(&dwritesf->mat_inverse, &x, &y); cairo_matrix_transform_point(&dwritesf->mat_inverse, &x, &y);
/** /**
* Since we will multiply by our ctm matrix later for rotation effects * Since we will multiply by our ctm matrix later for rotation effects
* and such, adjust positions by the inverse matrix now. The Y-axis * and such, adjust positions by the inverse matrix now. The Y-axis
* is inverted so the offset becomes negative. * is inverted so the offset becomes negative.
*/ */
offsets[i].ascenderOffset = -(FLOAT)y; offsets[i].ascenderOffset = -(FLOAT)y;
offsets[i].advanceOffset = (FLOAT)x; offsets[i].advanceOffset = (FLOAT)x;
advances[i] = 0.0; advances[i] = 0.0;
@ -1370,6 +1382,49 @@ _cairo_dwrite_show_glyphs_on_surface(void *surface,
return CAIRO_INT_STATUS_SUCCESS; return CAIRO_INT_STATUS_SUCCESS;
} }
#define ENHANCED_CONTRAST_REGISTRY_KEY \
HKEY_CURRENT_USER, "Software\\Microsoft\\Avalon.Graphics\\DISPLAY1\\EnhancedContrastLevel"
void
DWriteFactory::CreateRenderingParams()
{
if (!Instance()) {
return;
}
RefPtr<IDWriteRenderingParams> defaultParams;
Instance()->CreateRenderingParams(&defaultParams);
// For EnhancedContrast, we override the default if the user has not set it
// in the registry (by using the ClearType Tuner).
FLOAT contrast;
if (mEnhancedContrast >= 0.0 && mEnhancedContrast <= 10.0) {
contrast = mEnhancedContrast;
} else {
HKEY hKey;
if (RegOpenKeyExA(ENHANCED_CONTRAST_REGISTRY_KEY,
0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
contrast = defaultParams->GetEnhancedContrast();
RegCloseKey(hKey);
} else {
contrast = 1.0;
}
}
// For parameters that have not been explicitly set via the SetRenderingParams API,
// we copy values from default params (or our overridden value for contrast)
Instance()->CreateCustomRenderingParams(
mGamma >= 1.0 && mGamma <= 2.2 ? mGamma : defaultParams->GetGamma(),
contrast,
mClearTypeLevel >= 0.0 && mClearTypeLevel <= 1.0 ? mClearTypeLevel : defaultParams->GetClearTypeLevel(),
mPixelGeometry >= DWRITE_PIXEL_GEOMETRY_FLAT && mPixelGeometry <= DWRITE_PIXEL_GEOMETRY_BGR ?
(DWRITE_PIXEL_GEOMETRY)mPixelGeometry : defaultParams->GetPixelGeometry(),
mRenderingMode >= DWRITE_RENDERING_MODE_DEFAULT && mRenderingMode <= DWRITE_RENDERING_MODE_CLEARTYPE_NATURAL_SYMMETRIC ?
(DWRITE_RENDERING_MODE)mRenderingMode : defaultParams->GetRenderingMode(),
&mRenderingParams);
}
// Helper for _cairo_win32_printing_surface_show_glyphs to create a win32 equivalent // Helper for _cairo_win32_printing_surface_show_glyphs to create a win32 equivalent
// of a dwrite scaled_font so that we can print using ExtTextOut instead of drawing // of a dwrite scaled_font so that we can print using ExtTextOut instead of drawing
// paths or blitting glyph bitmaps. // paths or blitting glyph bitmaps.

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

@ -92,9 +92,46 @@ public:
return family; return family;
} }
static IDWriteRenderingParams *RenderingParams()
{
if (!mRenderingParams) {
CreateRenderingParams();
}
if (mRenderingParams) {
mRenderingParams->AddRef();
}
return mRenderingParams;
}
static void SetRenderingParams(FLOAT aGamma,
FLOAT aEnhancedContrast,
FLOAT aClearTypeLevel,
int aPixelGeometry,
int aRenderingMode)
{
mGamma = aGamma;
mEnhancedContrast = aEnhancedContrast;
mClearTypeLevel = aClearTypeLevel;
mPixelGeometry = aPixelGeometry;
mRenderingMode = aRenderingMode;
// discard any current RenderingParams object
if (mRenderingParams) {
mRenderingParams->Release();
mRenderingParams = NULL;
}
}
private: private:
static void CreateRenderingParams();
static IDWriteFactory *mFactoryInstance; static IDWriteFactory *mFactoryInstance;
static IDWriteFontCollection *mSystemCollection; static IDWriteFontCollection *mSystemCollection;
static IDWriteRenderingParams *mRenderingParams;
static FLOAT mGamma;
static FLOAT mEnhancedContrast;
static FLOAT mClearTypeLevel;
static int mPixelGeometry;
static int mRenderingMode;
}; };
/* cairo_font_face_t implementation */ /* cairo_font_face_t implementation */

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

@ -126,6 +126,9 @@ cairo_dwrite_font_face_create_for_dwrite_fontface(void *dwrite_font, void *dwrit
void void
cairo_dwrite_scaled_font_allow_manual_show_glyphs(void *dwrite_scaled_font, cairo_bool_t allowed); cairo_dwrite_scaled_font_allow_manual_show_glyphs(void *dwrite_scaled_font, cairo_bool_t allowed);
void
cairo_dwrite_set_cleartype_params(FLOAT gamma, FLOAT contrast, FLOAT level, int geometry, int mode);
#endif /* CAIRO_HAS_DWRITE_FONT */ #endif /* CAIRO_HAS_DWRITE_FONT */
#if CAIRO_HAS_D2D_SURFACE #if CAIRO_HAS_D2D_SURFACE

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

@ -149,7 +149,10 @@ gfxDWriteFont::gfxDWriteFont(gfxFontEntry *aFontEntry,
return; return;
} }
if ((anAAOption == gfxFont::kAntialiasDefault && UsingClearType()) || if ((anAAOption == gfxFont::kAntialiasDefault &&
UsingClearType() &&
(gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode() ==
DWRITE_MEASURING_MODE_NATURAL)) ||
anAAOption == gfxFont::kAntialiasSubpixel) anAAOption == gfxFont::kAntialiasSubpixel)
{ {
mUseSubpixelPositions = PR_TRUE; mUseSubpixelPositions = PR_TRUE;
@ -248,16 +251,12 @@ gfxDWriteFont::ComputeMetrics()
mMetrics = new gfxFont::Metrics; mMetrics = new gfxFont::Metrics;
::memset(mMetrics, 0, sizeof(*mMetrics)); ::memset(mMetrics, 0, sizeof(*mMetrics));
mMetrics->xHeight = mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm);
((gfxFloat)fontMetrics.xHeight /
fontMetrics.designUnitsPerEm) * mAdjustedSize;
mMetrics->maxAscent = mMetrics->xHeight = fontMetrics.xHeight * mFUnitsConvFactor;
ceil(((gfxFloat)fontMetrics.ascent /
fontMetrics.designUnitsPerEm) * mAdjustedSize); mMetrics->maxAscent = ceil(fontMetrics.ascent * mFUnitsConvFactor);
mMetrics->maxDescent = mMetrics->maxDescent = ceil(fontMetrics.descent * mFUnitsConvFactor);
ceil(((gfxFloat)fontMetrics.descent /
fontMetrics.designUnitsPerEm) * mAdjustedSize);
mMetrics->maxHeight = mMetrics->maxAscent + mMetrics->maxDescent; mMetrics->maxHeight = mMetrics->maxAscent + mMetrics->maxDescent;
mMetrics->emHeight = mAdjustedSize; mMetrics->emHeight = mAdjustedSize;
@ -282,88 +281,72 @@ gfxDWriteFont::ComputeMetrics()
if (exists && len >= sizeof(mozilla::HheaTable)) { if (exists && len >= sizeof(mozilla::HheaTable)) {
const mozilla::HheaTable* hhea = const mozilla::HheaTable* hhea =
reinterpret_cast<const mozilla::HheaTable*>(tableData); reinterpret_cast<const mozilla::HheaTable*>(tableData);
mMetrics->maxAdvance = ((gfxFloat)PRUint16(hhea->advanceWidthMax) / mMetrics->maxAdvance =
fontMetrics.designUnitsPerEm) * mAdjustedSize; PRUint16(hhea->advanceWidthMax) * mFUnitsConvFactor;
} }
mFontFace->ReleaseFontTable(tableContext); mFontFace->ReleaseFontTable(tableContext);
} }
mMetrics->internalLeading = NS_MAX(mMetrics->maxHeight - mMetrics->emHeight, 0.0); mMetrics->internalLeading = NS_MAX(mMetrics->maxHeight - mMetrics->emHeight, 0.0);
mMetrics->externalLeading = mMetrics->externalLeading = ceil(fontMetrics.lineGap * mFUnitsConvFactor);
ceil(((gfxFloat)fontMetrics.lineGap /
fontMetrics.designUnitsPerEm) * mAdjustedSize);
UINT16 glyph = (PRUint16)GetSpaceGlyph(); UINT16 glyph = (PRUint16)GetSpaceGlyph();
DWRITE_GLYPH_METRICS metrics; mMetrics->spaceWidth = MeasureGlyphWidth(glyph);
mFontFace->GetDesignGlyphMetrics(&glyph, 1, &metrics);
mMetrics->spaceWidth =
((gfxFloat)metrics.advanceWidth /
fontMetrics.designUnitsPerEm) * mAdjustedSize;
// try to get aveCharWidth from the OS/2 table, fall back to measuring 'x' // try to get aveCharWidth from the OS/2 table, fall back to measuring 'x'
// if the table is not available // if the table is not available or if using hinted/pixel-snapped widths
mMetrics->aveCharWidth = 0; if (mUseSubpixelPositions) {
hr = mFontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('O', 'S', '/', '2'), mMetrics->aveCharWidth = 0;
(const void**)&tableData, hr = mFontFace->TryGetFontTable(DWRITE_MAKE_OPENTYPE_TAG('O', 'S', '/', '2'),
&len, (const void**)&tableData,
&tableContext, &len,
&exists); &tableContext,
if (SUCCEEDED(hr)) { &exists);
if (exists && len >= 4) { if (SUCCEEDED(hr)) {
// Not checking against sizeof(mozilla::OS2Table) here because older if (exists && len >= 4) {
// versions of the table have different sizes; we only need the first // Not checking against sizeof(mozilla::OS2Table) here because older
// two 16-bit fields here. // versions of the table have different sizes; we only need the first
const mozilla::OS2Table* os2 = // two 16-bit fields here.
reinterpret_cast<const mozilla::OS2Table*>(tableData); const mozilla::OS2Table* os2 =
mMetrics->aveCharWidth = ((gfxFloat)PRInt16(os2->xAvgCharWidth) / reinterpret_cast<const mozilla::OS2Table*>(tableData);
fontMetrics.designUnitsPerEm) * mAdjustedSize; mMetrics->aveCharWidth =
PRInt16(os2->xAvgCharWidth) * mFUnitsConvFactor;
}
mFontFace->ReleaseFontTable(tableContext);
} }
mFontFace->ReleaseFontTable(tableContext);
} }
UINT32 ucs; UINT32 ucs;
if (mMetrics->aveCharWidth < 1) { if (mMetrics->aveCharWidth < 1) {
ucs = L'x'; ucs = L'x';
if (SUCCEEDED(mFontFace->GetGlyphIndicesA(&ucs, 1, &glyph)) && if (SUCCEEDED(mFontFace->GetGlyphIndicesA(&ucs, 1, &glyph))) {
SUCCEEDED(mFontFace->GetDesignGlyphMetrics(&glyph, 1, &metrics))) { mMetrics->aveCharWidth = MeasureGlyphWidth(glyph);
mMetrics->aveCharWidth = }
((gfxFloat)metrics.advanceWidth / if (mMetrics->aveCharWidth < 1) {
fontMetrics.designUnitsPerEm) * mAdjustedSize;
} else {
// Let's just assume the X is square. // Let's just assume the X is square.
mMetrics->aveCharWidth = mMetrics->aveCharWidth = fontMetrics.xHeight * mFUnitsConvFactor;
((gfxFloat)fontMetrics.xHeight /
fontMetrics.designUnitsPerEm) * mAdjustedSize;
} }
} }
ucs = L'0'; ucs = L'0';
if (SUCCEEDED(mFontFace->GetGlyphIndicesA(&ucs, 1, &glyph)) && if (SUCCEEDED(mFontFace->GetGlyphIndicesA(&ucs, 1, &glyph))) {
SUCCEEDED(mFontFace->GetDesignGlyphMetrics(&glyph, 1, &metrics))) { mMetrics->zeroOrAveCharWidth = MeasureGlyphWidth(glyph);
mMetrics->zeroOrAveCharWidth = }
((gfxFloat)metrics.advanceWidth / if (mMetrics->zeroOrAveCharWidth < 1) {
fontMetrics.designUnitsPerEm) * mAdjustedSize;
} else {
mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth; mMetrics->zeroOrAveCharWidth = mMetrics->aveCharWidth;
} }
mMetrics->underlineOffset = mMetrics->underlineOffset =
((gfxFloat)fontMetrics.underlinePosition / fontMetrics.underlinePosition * mFUnitsConvFactor;
fontMetrics.designUnitsPerEm) * mAdjustedSize;
mMetrics->underlineSize = mMetrics->underlineSize =
((gfxFloat)fontMetrics.underlineThickness / fontMetrics.underlineThickness * mFUnitsConvFactor;
fontMetrics.designUnitsPerEm) * mAdjustedSize; mMetrics->strikeoutOffset =
mMetrics->strikeoutOffset = fontMetrics.strikethroughPosition * mFUnitsConvFactor;
((gfxFloat)fontMetrics.strikethroughPosition / mMetrics->strikeoutSize =
fontMetrics.designUnitsPerEm) * mAdjustedSize; fontMetrics.strikethroughThickness * mFUnitsConvFactor;
mMetrics->strikeoutSize =
((gfxFloat)fontMetrics.strikethroughThickness /
fontMetrics.designUnitsPerEm) * mAdjustedSize;
mMetrics->superscriptOffset = 0; mMetrics->superscriptOffset = 0;
mMetrics->subscriptOffset = 0; mMetrics->subscriptOffset = 0;
mFUnitsConvFactor = float(mAdjustedSize / fontMetrics.designUnitsPerEm);
SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont); SanitizeMetrics(mMetrics, GetFontEntry()->mIsBadUnderlineFont);
#if 0 #if 0
@ -697,26 +680,30 @@ gfxDWriteFont::GetGlyphWidth(gfxContext *aCtx, PRUint16 aGID)
return width; return width;
} }
DWRITE_GLYPH_METRICS glyphMetrics; width = NS_lround(MeasureGlyphWidth(aGID) * 65536.0);
HRESULT hr;
if (mUseSubpixelPositions) {
hr = mFontFace->GetDesignGlyphMetrics(
&aGID, 1, &glyphMetrics, FALSE);
if (SUCCEEDED(hr)) {
width =
NS_lround(glyphMetrics.advanceWidth * mFUnitsConvFactor *
65536.0);
}
} else {
hr = mFontFace->GetGdiCompatibleGlyphMetrics(
FLOAT(mAdjustedSize), 1.0f, nsnull, FALSE,
&aGID, 1, &glyphMetrics, FALSE);
if (SUCCEEDED(hr)) {
width =
NS_lround(glyphMetrics.advanceWidth * mFUnitsConvFactor) << 16;
}
}
mGlyphWidths.Put(aGID, width); mGlyphWidths.Put(aGID, width);
return width; return width;
} }
gfxFloat
gfxDWriteFont::MeasureGlyphWidth(PRUint16 aGlyph)
{
DWRITE_GLYPH_METRICS metrics;
HRESULT hr;
if (mUseSubpixelPositions) {
hr = mFontFace->GetDesignGlyphMetrics(&aGlyph, 1, &metrics, FALSE);
if (SUCCEEDED(hr)) {
return metrics.advanceWidth * mFUnitsConvFactor;
}
} else {
hr = mFontFace->GetGdiCompatibleGlyphMetrics(
FLOAT(mAdjustedSize), 1.0f, nsnull,
gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode() ==
DWRITE_MEASURING_MODE_GDI_NATURAL,
&aGlyph, 1, &metrics, FALSE);
if (SUCCEEDED(hr)) {
return NS_lround(metrics.advanceWidth * mFUnitsConvFactor);
}
}
return 0;
}

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

@ -98,6 +98,8 @@ protected:
cairo_scaled_font_t *CairoScaledFont(); cairo_scaled_font_t *CairoScaledFont();
gfxFloat MeasureGlyphWidth(PRUint16 aGlyph);
static void DestroyBlobFunc(void* userArg); static void DestroyBlobFunc(void* userArg);
nsRefPtr<IDWriteFontFace> mFontFace; nsRefPtr<IDWriteFontFace> mFontFace;

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

@ -2666,7 +2666,6 @@ gfxFontGroup::GetGeneration()
void void
gfxFontGroup::UpdateFontList() gfxFontGroup::UpdateFontList()
{ {
// if user font set is set, check to see if font list needs updating
if (mUserFontSet && mCurrGeneration != GetGeneration()) { if (mUserFontSet && mCurrGeneration != GetGeneration()) {
// xxx - can probably improve this to detect when all fonts were found, so no need to update list // xxx - can probably improve this to detect when all fonts were found, so no need to update list
mFonts.Clear(); mFonts.Clear();

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

@ -58,6 +58,10 @@
#include "nsUnicodeRange.h" #include "nsUnicodeRange.h"
#include "nsCRT.h" #include "nsCRT.h"
#if defined(XP_WIN)
#include "gfxWindowsPlatform.h"
#endif
#define FloatToFixed(f) (65536 * (f)) #define FloatToFixed(f) (65536 * (f))
#define FixedToFloat(f) ((f) * (1.0 / 65536.0)) #define FixedToFloat(f) ((f) * (1.0 / 65536.0))
// Right shifts of negative (signed) integers are undefined, as are overflows // Right shifts of negative (signed) integers are undefined, as are overflows
@ -946,8 +950,12 @@ GetRoundOffsetsToPixels(gfxContext *aContext,
#if CAIRO_HAS_DWRITE_FONT // dwrite backend is not in std cairo releases yet #if CAIRO_HAS_DWRITE_FONT // dwrite backend is not in std cairo releases yet
case CAIRO_FONT_TYPE_DWRITE: case CAIRO_FONT_TYPE_DWRITE:
// show_glyphs is implemented on the font and so is used for // show_glyphs is implemented on the font and so is used for
// all surface types. // all surface types; however, it may pixel-snap depending on
return; // the dwrite rendering mode
if (gfxWindowsPlatform::GetPlatform()->DWriteMeasuringMode() ==
DWRITE_MEASURING_MODE_NATURAL) {
return;
}
#endif #endif
case CAIRO_FONT_TYPE_QUARTZ: case CAIRO_FONT_TYPE_QUARTZ:
// Quartz surfaces implement show_glyphs for Quartz fonts // Quartz surfaces implement show_glyphs for Quartz fonts

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

@ -158,6 +158,13 @@ NS_IMPL_ISUPPORTS1(D2DVRAMReporter, nsIMemoryReporter)
#define GFX_USE_CLEARTYPE_ALWAYS "gfx.font_rendering.cleartype.always_use_for_content" #define GFX_USE_CLEARTYPE_ALWAYS "gfx.font_rendering.cleartype.always_use_for_content"
#define GFX_DOWNLOADABLE_FONTS_USE_CLEARTYPE "gfx.font_rendering.cleartype.use_for_downloadable_fonts" #define GFX_DOWNLOADABLE_FONTS_USE_CLEARTYPE "gfx.font_rendering.cleartype.use_for_downloadable_fonts"
#define GFX_CLEARTYPE_PARAMS "gfx.font_rendering.cleartype_params."
#define GFX_CLEARTYPE_PARAMS_GAMMA "gfx.font_rendering.cleartype_params.gamma"
#define GFX_CLEARTYPE_PARAMS_CONTRAST "gfx.font_rendering.cleartype_params.enhanced_contrast"
#define GFX_CLEARTYPE_PARAMS_LEVEL "gfx.font_rendering.cleartype_params.cleartype_level"
#define GFX_CLEARTYPE_PARAMS_STRUCTURE "gfx.font_rendering.cleartype_params.pixel_structure"
#define GFX_CLEARTYPE_PARAMS_MODE "gfx.font_rendering.cleartype_params.rendering_mode"
#ifdef MOZ_FT2_FONTS #ifdef MOZ_FT2_FONTS
static FT_Library gPlatformFTLibrary = NULL; static FT_Library gPlatformFTLibrary = NULL;
#endif #endif
@ -343,6 +350,8 @@ gfxWindowsPlatform::UpdateRenderMode()
mDWriteFactory = factory; mDWriteFactory = factory;
factory->Release(); factory->Release();
SetupClearTypeParams(pref);
if (hr == S_OK) if (hr == S_OK)
reporter.SetSuccessful(); reporter.SetSuccessful();
} }
@ -790,6 +799,8 @@ gfxWindowsPlatform::FontsPrefsChanged(nsIPrefBranch *aPrefBranch, const char *aP
mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE; mUseClearTypeForDownloadableFonts = UNINITIALIZED_VALUE;
} else if (!strcmp(GFX_USE_CLEARTYPE_ALWAYS, aPref)) { } else if (!strcmp(GFX_USE_CLEARTYPE_ALWAYS, aPref)) {
mUseClearTypeAlways = UNINITIALIZED_VALUE; mUseClearTypeAlways = UNINITIALIZED_VALUE;
} else if (!strncmp(GFX_CLEARTYPE_PARAMS, aPref, strlen(GFX_CLEARTYPE_PARAMS))) {
SetupClearTypeParams(aPrefBranch);
} else { } else {
clearTextFontCaches = PR_FALSE; clearTextFontCaches = PR_FALSE;
} }
@ -803,6 +814,67 @@ gfxWindowsPlatform::FontsPrefsChanged(nsIPrefBranch *aPrefBranch, const char *aP
} }
} }
void
gfxWindowsPlatform::SetupClearTypeParams(nsIPrefBranch *aPrefBranch)
{
#if CAIRO_HAS_DWRITE_FONT
if (GetDWriteFactory()) {
// any missing prefs will default to invalid (-1) and be ignored;
// out-of-range values will also be ignored
FLOAT gamma = -1.0;
FLOAT contrast = -1.0;
FLOAT level = -1.0;
int geometry = -1;
int mode = -1;
PRInt32 value;
if (NS_SUCCEEDED(aPrefBranch->GetIntPref(GFX_CLEARTYPE_PARAMS_GAMMA,
&value))) {
if (value >= 1000 && value <= 2200) {
gamma = (FLOAT)value / 1000.0;
}
}
if (NS_SUCCEEDED(aPrefBranch->GetIntPref(GFX_CLEARTYPE_PARAMS_CONTRAST,
&value))) {
if (value >= 0 && value <= 1000) {
contrast = (FLOAT)value / 100.0;
}
}
if (NS_SUCCEEDED(aPrefBranch->GetIntPref(GFX_CLEARTYPE_PARAMS_LEVEL,
&value))) {
if (value >= 0 && value <= 100) {
level = (FLOAT)value / 100.0;
}
}
if (NS_SUCCEEDED(aPrefBranch->GetIntPref(GFX_CLEARTYPE_PARAMS_STRUCTURE,
&value))) {
if (value >= 0 && value <= 2) {
geometry = value;
}
}
if (NS_SUCCEEDED(aPrefBranch->GetIntPref(GFX_CLEARTYPE_PARAMS_MODE,
&value))) {
if (value >= 0 && value <= 5) {
mode = value;
}
}
cairo_dwrite_set_cleartype_params(gamma, contrast, level, geometry, mode);
switch (mode) {
case DWRITE_RENDERING_MODE_ALIASED:
case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_CLASSIC:
mMeasuringMode = DWRITE_MEASURING_MODE_GDI_CLASSIC;
break;
case DWRITE_RENDERING_MODE_CLEARTYPE_GDI_NATURAL:
mMeasuringMode = DWRITE_MEASURING_MODE_GDI_NATURAL;
break;
default:
mMeasuringMode = DWRITE_MEASURING_MODE_NATURAL;
break;
}
}
#endif
}
bool bool
gfxWindowsPlatform::IsOptimus() gfxWindowsPlatform::IsOptimus()
{ {

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

@ -238,9 +238,12 @@ public:
virtual void FontsPrefsChanged(nsIPrefBranch *aPrefBranch, const char *aPref); virtual void FontsPrefsChanged(nsIPrefBranch *aPrefBranch, const char *aPref);
void SetupClearTypeParams(nsIPrefBranch *aPrefBranch);
#ifdef CAIRO_HAS_DWRITE_FONT #ifdef CAIRO_HAS_DWRITE_FONT
IDWriteFactory *GetDWriteFactory() { return mDWriteFactory; } IDWriteFactory *GetDWriteFactory() { return mDWriteFactory; }
inline PRBool DWriteEnabled() { return mUseDirectWrite; } inline PRBool DWriteEnabled() { return mUseDirectWrite; }
inline DWRITE_MEASURING_MODE DWriteMeasuringMode() { return mMeasuringMode; }
#else #else
inline PRBool DWriteEnabled() { return PR_FALSE; } inline PRBool DWriteEnabled() { return PR_FALSE; }
#endif #endif
@ -270,6 +273,7 @@ private:
#ifdef CAIRO_HAS_DWRITE_FONT #ifdef CAIRO_HAS_DWRITE_FONT
nsRefPtr<IDWriteFactory> mDWriteFactory; nsRefPtr<IDWriteFactory> mDWriteFactory;
DWRITE_MEASURING_MODE mMeasuringMode;
#endif #endif
#ifdef CAIRO_HAS_D2D_SURFACE #ifdef CAIRO_HAS_D2D_SURFACE
cairo_device_t *mD2DDevice; cairo_device_t *mD2DDevice;

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

@ -82,11 +82,7 @@ else
ELOG := ELOG :=
endif endif
ifeq ($(OS_ARCH),WINNT)
_VPATH_SRCS = $(abspath $<) _VPATH_SRCS = $(abspath $<)
else
_VPATH_SRCS = $<
endif
# Add $(DIST)/lib to VPATH so that -lfoo dependencies are followed # Add $(DIST)/lib to VPATH so that -lfoo dependencies are followed
VPATH += $(DIST)/lib VPATH += $(DIST)/lib

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

@ -50,7 +50,7 @@
#elif defined(XP_MACOSX) || defined(DARWIN) || defined(XP_UNIX) #elif defined(XP_MACOSX) || defined(DARWIN) || defined(XP_UNIX)
# include <pthread.h> # include <pthread.h>
# if defined(__FreeBSD__) # if defined(__FreeBSD__) || defined(__OpenBSD__)
# include <pthread_np.h> # include <pthread_np.h>
# endif # endif

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

@ -446,8 +446,10 @@ public:
PRBool SendNativeEvents() PRBool SendNativeEvents()
{ {
#ifdef XP_WIN #ifdef XP_WIN
// XXX we should remove the plugin name check
return mPluginWindow->type == NPWindowTypeDrawable && return mPluginWindow->type == NPWindowTypeDrawable &&
MatchPluginName("Shockwave Flash"); (MatchPluginName("Shockwave Flash") ||
MatchPluginName("Test Plug-in"));
#elif defined(MOZ_X11) || defined(XP_MACOSX) #elif defined(MOZ_X11) || defined(XP_MACOSX)
return PR_TRUE; return PR_TRUE;
#else #else

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

@ -62,8 +62,8 @@ fails-if(Android) == 386339.html 386339-ref.html
== 409375.html 409375-ref.html == 409375.html 409375-ref.html
== 413542-1.html 413542-1-ref.html == 413542-1.html 413542-1-ref.html
== 413542-2.html 413542-2-ref.html == 413542-2.html 413542-2-ref.html
fails-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 413928-1.html 413928-1-ref.html == 413928-1.html 413928-1-ref.html
fails-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 413928-2.html 413928-2-ref.html == 413928-2.html 413928-2-ref.html
== 425338-1a.html 425338-1-ref.html == 425338-1a.html 425338-1-ref.html
== 425338-1b.html 425338-1-ref.html == 425338-1b.html 425338-1-ref.html
== 489517-1.html 489517-1-ref.html == 489517-1.html 489517-1-ref.html

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

@ -675,7 +675,7 @@ fails-if(Android) != 376532-3.html 376532-3-ref.html
== 379178-html.html 379178-html-ref.html == 379178-html.html 379178-html-ref.html
== 379178-svg.svg 379178-svg-ref.svg == 379178-svg.svg 379178-svg-ref.svg
== 379316-1.html 379316-1-ref.html == 379316-1.html 379316-1-ref.html
fails-if(Android) random-if(cocoaWidget) == 379316-2.html 379316-2-ref.html # bug 379786 fails-if(Android) random-if(cocoaWidget) fails-if(/^Windows\x20NT\x206\.1/.test(http.oscpu)) == 379316-2.html 379316-2-ref.html # bug 379786
== 379328-1.html 379328-1-ref.html == 379328-1.html 379328-1-ref.html
== 379349-1a.xhtml 379349-1-ref.xhtml == 379349-1a.xhtml 379349-1-ref.xhtml
== 379349-1b.xhtml 379349-1-ref.xhtml == 379349-1b.xhtml 379349-1-ref.xhtml

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

@ -196,6 +196,8 @@ fails-if(Android) random-if(gtk2Widget) != text-language-01.xhtml text-language-
== text-layout-03.svg text-layout-03-ref.svg == text-layout-03.svg text-layout-03-ref.svg
== text-scale-01.svg text-scale-01-ref.svg == text-scale-01.svg text-scale-01-ref.svg
== text-stroke-scaling-01.svg text-stroke-scaling-01-ref.svg == text-stroke-scaling-01.svg text-stroke-scaling-01-ref.svg
== stroke-linecap-square-w-zero-length-segs-01.svg pass.svg
== stroke-linecap-square-w-zero-length-segs-02.svg pass.svg
== text-style-01a.svg text-style-01-ref.svg == text-style-01a.svg text-style-01-ref.svg
== text-style-01b.svg text-style-01-ref.svg == text-style-01b.svg text-style-01-ref.svg
== text-style-01c.svg text-style-01-ref.svg == text-style-01c.svg text-style-01-ref.svg

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

@ -0,0 +1,143 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<svg xmlns="http://www.w3.org/2000/svg">
<title>Test 'stroke-linecap: square' with zero length path segments</title>
<!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=589648 -->
<style>
path {
stroke-width: 20px;
stroke-linecap: square;
}
rect {
fill: red;
}
/* expect lime squares to cover red rects */
path.squares-expected {
stroke: lime;
}
path.squares-not-expected {
stroke: red;
}
/* thicker stroke to cover squares-not-expected paths */
path.coverer {
stroke: lime;
stroke-width: 24px;
}
/* to show edges of shapes to help in debugging:
g > rect {
stroke: red;
stroke-width: 5px;
}
path.coverer {
stroke: lime;
stroke-width: 18px;
}
*/
</style>
<rect width="100%" height="100%" style="fill:lime"/>
<!-- Column 1: test single segment zero-length subpaths: -->
<g transform="translate(25,25)">
<rect x="-9" y="-9" width="18" height="18"/>
<rect x="41" y="41" width="18" height="18"/>
<rect x="91" y="91" width="18" height="18"/>
<path class="squares-expected" d="M0,0 L0,0 M20,20 L30,30 M50,50 L50,50 M70,70 L80,80 M100,100 L100,100"/>
</g>
<g transform="translate(25,75)">
<rect x="-9" y="-9" width="18" height="18"/>
<rect x="41" y="41" width="18" height="18"/>
<rect x="91" y="91" width="18" height="18"/>
<path class="squares-expected" d="M0,0 C0,0 0,0 0,0 M20,20 L30,30 M50,50 C50,50 50,50 50,50 M70,70 L80,80 M100,100 C100,100 100,100 100,100"/>
</g>
<g transform="translate(25,125)">
<path class="squares-not-expected" d="M0,0 A0,10 0 0 0 0,0 M20,20 L30,30 M50,50 A0,10 0 0 0 50,50 M70,70 L80,80 M100,100 A0,10 0 0 0 100,100"/>
<path class="coverer" d="M20,20 L30,30 M70,70 L80,80"/>
</g>
<g transform="translate(25,175)">
<rect x="-9" y="-9" width="18" height="18"/>
<rect x="41" y="41" width="18" height="18"/>
<rect x="91" y="91" width="18" height="18"/>
<path class="squares-expected" d="M0,0 Z M20,20 L30,30 M50,50 Z M70,70 L80,80 M100,100 Z"/>
</g>
<!-- Column 2: test multi-segment zero-length subpaths: -->
<g transform="translate(175,25)">
<rect x="-9" y="-9" width="18" height="18"/>
<rect x="41" y="41" width="18" height="18"/>
<rect x="91" y="91" width="18" height="18"/>
<path class="squares-expected" d="M0,0 L0,0 M0,0 L0,0 M20,20 L30,30 M50,50 L50,50 L50,50 M70,70 L80,80 M100,100 L100,100 L100,100"/>
</g>
<g transform="translate(177,75)">
<rect x="-9" y="-9" width="18" height="18"/>
<rect x="41" y="41" width="18" height="18"/>
<rect x="91" y="91" width="18" height="18"/>
<path class="squares-expected" d="M0,0 C0,0 0,0 0,0 C0,0 0,0 0,0 M20,20 L30,30 M50,50 C50,50 50,50 50,50 C50,50 50,50 50,50 M70,70 L80,80 M100,100 C100,100 100,100 100,100 C100,100 100,100 100,100"/>
</g>
<g transform="translate(175,125)">
<path class="squares-not-expected" d="M0,0 A0,10 0 0 0 0,0 A0,10 0 0 0 0,0 M20,20 L30,30 M50,50 A0,10 0 0 0 50,50 A0,10 0 0 0 50,50 M70,70 L80,80 M100,100 A0,10 0 0 0 100,100 A0,10 0 0 0 100,100"/>
<path class="coverer" d="M20,20 L30,30 M70,70 L80,80"/>
</g>
<g transform="translate(175,175)">
<rect x="-9" y="-9" width="18" height="18"/>
<rect x="41" y="41" width="18" height="18"/>
<rect x="91" y="91" width="18" height="18"/>
<path class="squares-expected" d="M0,0 Z Z M20,20 L30,30 M50,50 Z Z M70,70 L80,80 M100,100 Z Z"/>
</g>
<!-- Column 3: test non-zero-length subpaths that begin, end and contain
zero length segments: -->
<g transform="translate(325,25)">
<path class="squares-not-expected" d="M20,20 L20,20 L30,30 L30,30 L40,40 L40,40"/>
<path class="coverer" d="M20,20 L40,40"/>
</g>
<g transform="translate(325,75)">
<path class="squares-not-expected" d="M20,20 C20,20 20,20 20,20 C20,20 30,30 30,30 C30,30 30,30 30,30 C30,30 40,40 40,40 C40,40 40,40 40,40"/>
<path class="coverer" d="M20,20 L40,40"/>
</g>
<g transform="translate(325,125)">
<path class="squares-not-expected" d="M20,20 A0,10 0 0 0 20,20 A0,10 0 0 0 30,30 A0,10 0 0 0 30,30 A0,10 0 0 0 40,40 A0,10 0 0 0 40,40"/>
<path class="coverer" d="M20,20 L40,40"/>
</g>
<!-- this one is shorter because the Z's mean we only have path end points
at 20,20 -->
<g transform="translate(325,175)">
<rect x="11" y="11" width="18" height="18"/>
<path class="squares-expected" d="M20,20 Z L30,30 Z L40,40 Z"/>
</g>
<!-- Column 4: test loan movetos -->
<g transform="translate(425,25)">
<path class="squares-not-expected" d="M0,0 M0,0 M20,20 L30,30 M50,50 M50,50 M70,70 L80,80 M100,100 M100,100"/>
<path class="coverer" d="M20,20 L30,30 M70,70 L80,80"/>
</g>
</svg>

После

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

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

@ -0,0 +1,28 @@
<!--
Any copyright is dedicated to the Public Domain.
http://creativecommons.org/licenses/publicdomain/
-->
<svg xmlns="http://www.w3.org/2000/svg" class="reftest-wait">
<title>Test 'stroke-linecap: square' with zero length path segments</title>
<!-- From https://bugzilla.mozilla.org/show_bug.cgi?id=589648 -->
<script>
function run()
{
document.getElementById('path').setAttribute('stroke-linecap', 'butt');
document.documentElement.removeAttribute('class');
}
window.addEventListener("MozReftestInvalidate", run, false);
</script>
<rect width="100%" height="100%" style="fill:lime"/>
<path id="path" stroke="red" stroke-width="200" stroke-linecap="square"
d="M100,100 L100,100"/>
</svg>

После

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

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

@ -105,11 +105,6 @@ public:
NS_IMETHOD NotifyRedrawSuspended()=0; NS_IMETHOD NotifyRedrawSuspended()=0;
NS_IMETHOD NotifyRedrawUnsuspended()=0; NS_IMETHOD NotifyRedrawUnsuspended()=0;
// Set whether we should stop multiplying matrices when building up
// the current transformation matrix at this frame.
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate)=0;
virtual PRBool GetMatrixPropagation()=0;
/** /**
* Get this frame's contribution to the rect returned by a GetBBox() call * Get this frame's contribution to the rect returned by a GetBBox() call
* that occurred either on this element, or on one of its ancestors. * that occurred either on this element, or on one of its ancestors.

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

@ -93,7 +93,7 @@ nsSVGContainerFrame::Init(nsIContent* aContent,
nsIFrame* aParent, nsIFrame* aParent,
nsIFrame* aPrevInFlow) nsIFrame* aPrevInFlow)
{ {
AddStateBits(NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_PROPAGATE_TRANSFORM); AddStateBits(NS_STATE_SVG_NONDISPLAY_CHILD);
nsresult rv = nsSVGContainerFrameBase::Init(aContent, aParent, aPrevInFlow); nsresult rv = nsSVGContainerFrameBase::Init(aContent, aParent, aPrevInFlow);
return rv; return rv;
} }
@ -103,7 +103,6 @@ nsSVGDisplayContainerFrame::Init(nsIContent* aContent,
nsIFrame* aParent, nsIFrame* aParent,
nsIFrame* aPrevInFlow) nsIFrame* aPrevInFlow)
{ {
AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
if (!(GetStateBits() & NS_STATE_IS_OUTER_SVG)) { if (!(GetStateBits() & NS_STATE_IS_OUTER_SVG)) {
AddStateBits(aParent->GetStateBits() & AddStateBits(aParent->GetStateBits() &
(NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD)); (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD));
@ -283,20 +282,3 @@ nsSVGDisplayContainerFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspac
return bboxUnion; return bboxUnion;
} }
NS_IMETHODIMP
nsSVGDisplayContainerFrame::SetMatrixPropagation(PRBool aPropagate)
{
if (aPropagate) {
AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
} else {
RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
}
return NS_OK;
}
PRBool
nsSVGDisplayContainerFrame::GetMatrixPropagation()
{
return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0;
}

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

@ -112,8 +112,6 @@ public:
virtual void NotifySVGChanged(PRUint32 aFlags); virtual void NotifySVGChanged(PRUint32 aFlags);
NS_IMETHOD NotifyRedrawSuspended(); NS_IMETHOD NotifyRedrawSuspended();
NS_IMETHOD NotifyRedrawUnsuspended(); NS_IMETHOD NotifyRedrawUnsuspended();
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
virtual PRBool GetMatrixPropagation();
virtual gfxRect GetBBoxContribution(const gfxMatrix &aToBBoxUserspace); virtual gfxRect GetBBoxContribution(const gfxMatrix &aToBBoxUserspace);
NS_IMETHOD_(PRBool) IsDisplayContainer() { return PR_TRUE; } NS_IMETHOD_(PRBool) IsDisplayContainer() { return PR_TRUE; }
NS_IMETHOD_(PRBool) HasValidCoveredRect() { return PR_FALSE; } NS_IMETHOD_(PRBool) HasValidCoveredRect() { return PR_FALSE; }

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

@ -91,9 +91,8 @@ nsSVGForeignObjectFrame::Init(nsIContent* aContent,
#endif #endif
nsresult rv = nsSVGForeignObjectFrameBase::Init(aContent, aParent, aPrevInFlow); nsresult rv = nsSVGForeignObjectFrameBase::Init(aContent, aParent, aPrevInFlow);
AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM | AddStateBits(aParent->GetStateBits() &
(aParent->GetStateBits() & (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD));
(NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD)));
if (NS_SUCCEEDED(rv)) { if (NS_SUCCEEDED(rv)) {
nsSVGUtils::GetOuterSVGFrame(this)->RegisterForeignObject(this); nsSVGUtils::GetOuterSVGFrame(this)->RegisterForeignObject(this);
} }
@ -432,23 +431,6 @@ nsSVGForeignObjectFrame::NotifyRedrawUnsuspended()
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsSVGForeignObjectFrame::SetMatrixPropagation(PRBool aPropagate)
{
if (aPropagate) {
AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
} else {
RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
}
return NS_OK;
}
PRBool
nsSVGForeignObjectFrame::GetMatrixPropagation()
{
return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0;
}
gfxRect gfxRect
nsSVGForeignObjectFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace) nsSVGForeignObjectFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace)
{ {

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

@ -128,8 +128,6 @@ public:
virtual void NotifySVGChanged(PRUint32 aFlags); virtual void NotifySVGChanged(PRUint32 aFlags);
NS_IMETHOD NotifyRedrawSuspended(); NS_IMETHOD NotifyRedrawSuspended();
NS_IMETHOD NotifyRedrawUnsuspended(); NS_IMETHOD NotifyRedrawUnsuspended();
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
virtual PRBool GetMatrixPropagation();
virtual gfxRect GetBBoxContribution(const gfxMatrix &aToBBoxUserspace); virtual gfxRect GetBBoxContribution(const gfxMatrix &aToBBoxUserspace);
NS_IMETHOD_(PRBool) IsDisplayContainer() { return PR_TRUE; } NS_IMETHOD_(PRBool) IsDisplayContainer() { return PR_TRUE; }
NS_IMETHOD_(PRBool) HasValidCoveredRect() { return PR_TRUE; } NS_IMETHOD_(PRBool) HasValidCoveredRect() { return PR_TRUE; }

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

@ -52,9 +52,8 @@ nsSVGGeometryFrame::Init(nsIContent* aContent,
nsIFrame* aParent, nsIFrame* aParent,
nsIFrame* aPrevInFlow) nsIFrame* aPrevInFlow)
{ {
AddStateBits((aParent->GetStateBits() & AddStateBits(aParent->GetStateBits() &
(NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD)) | (NS_STATE_SVG_NONDISPLAY_CHILD | NS_STATE_SVG_CLIPPATH_CHILD));
NS_STATE_SVG_PROPAGATE_TRANSFORM);
nsresult rv = nsSVGGeometryFrameBase::Init(aContent, aParent, aPrevInFlow); nsresult rv = nsSVGGeometryFrameBase::Init(aContent, aParent, aPrevInFlow);
return rv; return rv;
} }

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

@ -491,11 +491,11 @@ nsSVGGlyphFrame::UpdateCoveredRegion()
return NS_OK; return NS_OK;
} }
SetMatrixPropagation(PR_FALSE); mPropagateTransform = PR_FALSE;
CharacterIterator iter(this, PR_TRUE); CharacterIterator iter(this, PR_TRUE);
iter.SetInitialMatrix(tmpCtx); iter.SetInitialMatrix(tmpCtx);
AddBoundingBoxesToPath(&iter, tmpCtx); AddBoundingBoxesToPath(&iter, tmpCtx);
SetMatrixPropagation(PR_TRUE); mPropagateTransform = PR_TRUE;
tmpCtx->IdentityMatrix(); tmpCtx->IdentityMatrix();
// Be careful when replacing the following logic to get the fill and stroke // Be careful when replacing the following logic to get the fill and stroke
@ -1479,7 +1479,7 @@ nsSVGGlyphFrame::NotifyGlyphMetricsChange()
PRBool PRBool
nsSVGGlyphFrame::GetGlobalTransform(gfxMatrix *aMatrix) nsSVGGlyphFrame::GetGlobalTransform(gfxMatrix *aMatrix)
{ {
if (!GetMatrixPropagation()) { if (!mPropagateTransform) {
aMatrix->Reset(); aMatrix->Reset();
return PR_TRUE; return PR_TRUE;
} }
@ -1635,23 +1635,6 @@ nsSVGGlyphFrame::EnsureTextRun(float *aDrawScale, float *aMetricsScale,
return PR_TRUE; return PR_TRUE;
} }
NS_IMETHODIMP
nsSVGGlyphFrame::SetMatrixPropagation(PRBool aPropagate)
{
if (aPropagate) {
AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
} else {
RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
}
return NS_OK;
}
PRBool
nsSVGGlyphFrame::GetMatrixPropagation()
{
return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0;
}
//---------------------------------------------------------------------- //----------------------------------------------------------------------
// helper class // helper class

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

@ -66,7 +66,8 @@ protected:
: nsSVGGlyphFrameBase(aContext), : nsSVGGlyphFrameBase(aContext),
mTextRun(nsnull), mTextRun(nsnull),
mStartIndex(0), mStartIndex(0),
mWhitespaceHandling(COMPRESS_WHITESPACE) mWhitespaceHandling(COMPRESS_WHITESPACE),
mPropagateTransform(PR_TRUE)
{} {}
~nsSVGGlyphFrame() ~nsSVGGlyphFrame()
{ {
@ -126,8 +127,6 @@ public:
virtual void NotifySVGChanged(PRUint32 aFlags); virtual void NotifySVGChanged(PRUint32 aFlags);
NS_IMETHOD NotifyRedrawSuspended(); NS_IMETHOD NotifyRedrawSuspended();
NS_IMETHOD NotifyRedrawUnsuspended(); NS_IMETHOD NotifyRedrawUnsuspended();
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
virtual PRBool GetMatrixPropagation();
NS_IMETHOD_(PRBool) IsDisplayContainer() { return PR_FALSE; } NS_IMETHOD_(PRBool) IsDisplayContainer() { return PR_FALSE; }
NS_IMETHOD_(PRBool) HasValidCoveredRect() { return PR_TRUE; } NS_IMETHOD_(PRBool) HasValidCoveredRect() { return PR_TRUE; }
@ -229,6 +228,7 @@ protected:
// The start index into the position and rotation data // The start index into the position and rotation data
PRUint32 mStartIndex; PRUint32 mStartIndex;
PRUint8 mWhitespaceHandling; PRUint8 mWhitespaceHandling;
PRPackedBool mPropagateTransform;
}; };
#endif #endif

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

@ -335,23 +335,6 @@ nsSVGPathGeometryFrame::NotifyRedrawUnsuspended()
return NS_OK; return NS_OK;
} }
NS_IMETHODIMP
nsSVGPathGeometryFrame::SetMatrixPropagation(PRBool aPropagate)
{
if (aPropagate) {
AddStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
} else {
RemoveStateBits(NS_STATE_SVG_PROPAGATE_TRANSFORM);
}
return NS_OK;
}
PRBool
nsSVGPathGeometryFrame::GetMatrixPropagation()
{
return (GetStateBits() & NS_STATE_SVG_PROPAGATE_TRANSFORM) != 0;
}
gfxRect gfxRect
nsSVGPathGeometryFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace) nsSVGPathGeometryFrame::GetBBoxContribution(const gfxMatrix &aToBBoxUserspace)
{ {
@ -500,6 +483,12 @@ nsSVGPathGeometryFrame::GeneratePath(gfxContext* aContext,
aContext->Multiply(matrix); aContext->Multiply(matrix);
// Hack to let SVGPathData::ConstructPath know if we have square caps:
const nsStyleSVG* style = GetStyleSVG();
if (style->mStrokeLinecap == NS_STYLE_STROKE_LINECAP_SQUARE) {
aContext->SetLineCap(gfxContext::LINE_CAP_SQUARE);
}
aContext->NewPath(); aContext->NewPath();
static_cast<nsSVGPathGeometryElement*>(mContent)->ConstructPath(aContext); static_cast<nsSVGPathGeometryElement*>(mContent)->ConstructPath(aContext);
} }

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

@ -100,8 +100,6 @@ protected:
virtual void NotifySVGChanged(PRUint32 aFlags); virtual void NotifySVGChanged(PRUint32 aFlags);
NS_IMETHOD NotifyRedrawSuspended(); NS_IMETHOD NotifyRedrawSuspended();
NS_IMETHOD NotifyRedrawUnsuspended(); NS_IMETHOD NotifyRedrawUnsuspended();
NS_IMETHOD SetMatrixPropagation(PRBool aPropagate);
virtual PRBool GetMatrixPropagation();
virtual gfxRect GetBBoxContribution(const gfxMatrix &aToBBoxUserspace); virtual gfxRect GetBBoxContribution(const gfxMatrix &aToBBoxUserspace);
NS_IMETHOD_(PRBool) IsDisplayContainer() { return PR_FALSE; } NS_IMETHOD_(PRBool) IsDisplayContainer() { return PR_FALSE; }
NS_IMETHOD_(PRBool) HasValidCoveredRect() { return PR_TRUE; } NS_IMETHOD_(PRBool) HasValidCoveredRect() { return PR_TRUE; }

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

@ -98,10 +98,8 @@ class Element;
/* are we the child of a non-display container? */ /* are we the child of a non-display container? */
#define NS_STATE_SVG_NONDISPLAY_CHILD NS_FRAME_STATE_BIT(22) #define NS_STATE_SVG_NONDISPLAY_CHILD NS_FRAME_STATE_BIT(22)
#define NS_STATE_SVG_PROPAGATE_TRANSFORM NS_FRAME_STATE_BIT(23)
// If this bit is set, we are a <clipPath> element or descendant. // If this bit is set, we are a <clipPath> element or descendant.
#define NS_STATE_SVG_CLIPPATH_CHILD NS_FRAME_STATE_BIT(24) #define NS_STATE_SVG_CLIPPATH_CHILD NS_FRAME_STATE_BIT(23)
/** /**
* Byte offsets of channels in a native packed gfxColor or cairo image surface. * Byte offsets of channels in a native packed gfxColor or cairo image surface.

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

@ -1753,6 +1753,39 @@ pref("gfx.font_rendering.cleartype.use_for_downloadable_fonts", true);
// use cleartype rendering for all fonts always (win xp only) // use cleartype rendering for all fonts always (win xp only)
pref("gfx.font_rendering.cleartype.always_use_for_content", false); pref("gfx.font_rendering.cleartype.always_use_for_content", false);
// ClearType tuning parameters for directwrite/d2d.
//
// Allows overriding of underlying registry values in:
// HKCU/Software/Microsoft/Avalon.Graphics/<display> (contrast and level)
// HKLM/Software/Microsoft/Avalon.Graphics/<display> (gamma, pixel structure)
// and selection of the ClearType/antialiasing mode.
//
// A value of -1 implies use the default value, otherwise value ranges
// follow registry settings:
// gamma [1000, 2200] default: based on screen, typically 2200 (== 2.2)
// enhanced contrast [0, 1000] default: 50
// cleartype level [0, 100] default: 100
// pixel structure [0, 2] default: 0 (flat/RGB/BGR)
// rendering mode [0, 5] default: 0
// 0 = use default for font & size;
// 1 = aliased;
// 2 = GDI Classic;
// 3 = GDI Natural Widths;
// 4 = Natural;
// 5 = Natural Symmetric
//
// See:
// http://msdn.microsoft.com/en-us/library/aa970267.aspx
// http://msdn.microsoft.com/en-us/library/dd368190%28v=VS.85%29.aspx
// Note: DirectWrite uses the "Enhanced Contrast Level" value rather than the
// "Text Contrast Level" value
pref("gfx.font_rendering.cleartype_params.gamma", -1);
pref("gfx.font_rendering.cleartype_params.enhanced_contrast", -1);
pref("gfx.font_rendering.cleartype_params.cleartype_level", -1);
pref("gfx.font_rendering.cleartype_params.pixel_structure", -1);
pref("gfx.font_rendering.cleartype_params.rendering_mode", -1);
pref("ui.key.menuAccessKeyFocuses", true); pref("ui.key.menuAccessKeyFocuses", true);
// override double-click word selection behavior. // override double-click word selection behavior.

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

@ -4,13 +4,9 @@
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<body> <body>
<iframe id="iframe1" src="crashing_subpage.html" width="600" height="600"></iframe>
<script class="testbody" type="application/javascript"> <script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
var iframe = document.getElementById('iframe1');
window.frameLoaded = function frameLoaded_toCrash() { window.frameLoaded = function frameLoaded_toCrash() {
if (!SimpleTest.testPluginIsOOP()) { if (!SimpleTest.testPluginIsOOP()) {
ok(true, "Skipping this test when test plugin is not OOP."); ok(true, "Skipping this test when test plugin is not OOP.");
@ -18,6 +14,7 @@
return; return;
} }
var iframe = document.getElementById('iframe1');
var p = iframe.contentDocument.getElementById('plugin1'); var p = iframe.contentDocument.getElementById('plugin1');
// This test is for bug 550026, which is inherently nondeterministic. // This test is for bug 550026, which is inherently nondeterministic.
@ -50,3 +47,4 @@
} }
</script> </script>
<iframe id="iframe1" src="crashing_subpage.html" width="600" height="600"></iframe>

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

@ -4,13 +4,9 @@
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<body> <body>
<iframe id="iframe1" src="crashing_subpage.html" width="600" height="600"></iframe>
<script class="testbody" type="application/javascript"> <script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
var iframe = document.getElementById('iframe1');
window.frameLoaded = function frameLoaded_toCrash() { window.frameLoaded = function frameLoaded_toCrash() {
if (!SimpleTest.testPluginIsOOP()) { if (!SimpleTest.testPluginIsOOP()) {
ok(true, "Skipping this test when test plugin is not OOP."); ok(true, "Skipping this test when test plugin is not OOP.");
@ -18,6 +14,7 @@
return; return;
} }
var iframe = document.getElementById('iframe1');
var p = iframe.contentDocument.getElementById('plugin1'); var p = iframe.contentDocument.getElementById('plugin1');
p.setColor("FFFF00FF"); p.setColor("FFFF00FF");
@ -65,3 +62,4 @@
} }
</script> </script>
<iframe id="iframe1" src="crashing_subpage.html" width="600" height="600"></iframe>

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

@ -4,13 +4,9 @@
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script> <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<body> <body>
<iframe id="iframe1" src="crashing_subpage.html" width="600" height="600"></iframe>
<script class="testbody" type="application/javascript"> <script class="testbody" type="application/javascript">
SimpleTest.waitForExplicitFinish(); SimpleTest.waitForExplicitFinish();
var iframe = document.getElementById('iframe1');
window.frameLoaded = function frameLoaded_toCrash() { window.frameLoaded = function frameLoaded_toCrash() {
if (!SimpleTest.testPluginIsOOP()) { if (!SimpleTest.testPluginIsOOP()) {
ok(true, "Skipping this test when test plugin is not OOP."); ok(true, "Skipping this test when test plugin is not OOP.");
@ -26,6 +22,7 @@
var timeoutPref = "dom.ipc.plugins.timeoutSecs"; var timeoutPref = "dom.ipc.plugins.timeoutSecs";
prefs.setIntPref(timeoutPref, 5); prefs.setIntPref(timeoutPref, 5);
var iframe = document.getElementById('iframe1');
var p = iframe.contentDocument.getElementById('plugin1'); var p = iframe.contentDocument.getElementById('plugin1');
p.setColor("FFFF00FF"); p.setColor("FFFF00FF");
@ -74,3 +71,4 @@
} }
</script> </script>
<iframe id="iframe1" src="crashing_subpage.html" width="600" height="600"></iframe>

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

@ -225,6 +225,13 @@ getClipRegionRectCount(), this will throw an error. The coordinates are
the same as for getEdge. See getClipRegionRectCount() above for the same as for getEdge. See getClipRegionRectCount() above for
notes on platform plugin limitations. notes on platform plugin limitations.
== Keyboard events ==
* getLastKeyText()
Returns the text which was inputted by last keyboard events. This is cleared at
every keydown event.
NOTE: Currently, this is implemented only on Windows.
== Mouse events == == Mouse events ==
The test plugin supports the following scriptable methods: The test plugin supports the following scriptable methods:

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

@ -165,6 +165,7 @@ static bool getWindowPosition(NPObject* npobj, const NPVariant* args, uint32_t a
static bool constructObject(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static bool constructObject(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static bool setSitesWithData(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static bool setSitesWithData(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static bool setSitesWithDataCapabilities(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result); static bool setSitesWithDataCapabilities(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static bool getLastKeyText(NPObject* npobj, const NPVariant* args, uint32_t argCount, NPVariant* result);
static const NPUTF8* sPluginMethodIdentifierNames[] = { static const NPUTF8* sPluginMethodIdentifierNames[] = {
"npnEvaluateTest", "npnEvaluateTest",
@ -221,7 +222,8 @@ static const NPUTF8* sPluginMethodIdentifierNames[] = {
"getWindowPosition", "getWindowPosition",
"constructObject", "constructObject",
"setSitesWithData", "setSitesWithData",
"setSitesWithDataCapabilities" "setSitesWithDataCapabilities",
"getLastKeyText"
}; };
static NPIdentifier sPluginMethodIdentifiers[ARRAY_LENGTH(sPluginMethodIdentifierNames)]; static NPIdentifier sPluginMethodIdentifiers[ARRAY_LENGTH(sPluginMethodIdentifierNames)];
static const ScriptableFunction sPluginMethodFunctions[] = { static const ScriptableFunction sPluginMethodFunctions[] = {
@ -279,7 +281,8 @@ static const ScriptableFunction sPluginMethodFunctions[] = {
getWindowPosition, getWindowPosition,
constructObject, constructObject,
setSitesWithData, setSitesWithData,
setSitesWithDataCapabilities setSitesWithDataCapabilities,
getLastKeyText
}; };
STATIC_ASSERT(ARRAY_LENGTH(sPluginMethodIdentifierNames) == STATIC_ASSERT(ARRAY_LENGTH(sPluginMethodIdentifierNames) ==
@ -3436,3 +3439,15 @@ bool setSitesWithDataCapabilities(NPObject* npobj, const NPVariant* args, uint32
return true; return true;
} }
bool getLastKeyText(NPObject* npobj, const NPVariant* args, uint32_t argCount,
NPVariant* result)
{
if (argCount != 0) {
return false;
}
NPP npp = static_cast<TestNPObject*>(npobj)->npp;
InstanceData* id = static_cast<InstanceData*>(npp->pdata);
STRINGZ_TO_NPVARIANT(NPN_StrDup(id->lastKeyText.c_str()), *result);
return true;
}

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

@ -138,6 +138,7 @@ typedef struct InstanceData {
int32_t focusEventCount; int32_t focusEventCount;
int32_t eventModel; int32_t eventModel;
bool closeStream; bool closeStream;
std::string lastKeyText;
} InstanceData; } InstanceData;
void notifyDidPaint(InstanceData* instanceData); void notifyDidPaint(InstanceData* instanceData);

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

@ -458,6 +458,27 @@ handleEventInternal(InstanceData* instanceData, NPEvent* pe, LRESULT* result)
return true; return true;
} }
case WM_KEYDOWN:
instanceData->lastKeyText.erase();
*result = 0;
return true;
case WM_CHAR: {
*result = 0;
wchar_t uniChar = static_cast<wchar_t>(pe->wParam);
if (!uniChar) {
return true;
}
char utf8Char[6];
int len =
::WideCharToMultiByte(CP_UTF8, 0, &uniChar, 1, utf8Char, 6, NULL, NULL);
if (len == 0 || len > 6) {
return true;
}
instanceData->lastKeyText.append(utf8Char, len);
return true;
}
default: default:
return false; return false;
} }

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

@ -14,7 +14,7 @@ function makeEngine() {
} }
var syncTesting = new SyncTestingInfrastructure(makeEngine); var syncTesting = new SyncTestingInfrastructure(makeEngine);
function test_ID_caching() { add_test(function test_ID_caching() {
_("Ensure that Places IDs are not cached."); _("Ensure that Places IDs are not cached.");
let engine = new BookmarksEngine(); let engine = new BookmarksEngine();
@ -50,12 +50,12 @@ function test_ID_caching() {
do_check_eq(newMobileID, store.idForGUID("mobile", false)); do_check_eq(newMobileID, store.idForGUID("mobile", false));
do_check_eq(store.GUIDForId(mobileID), "abcdefghijkl"); do_check_eq(store.GUIDForId(mobileID), "abcdefghijkl");
} run_next_test();
});
function test_processIncoming_error_orderChildren() { add_test(function test_processIncoming_error_orderChildren() {
_("Ensure that _orderChildren() is called even when _processIncoming() throws an error."); _("Ensure that _orderChildren() is called even when _processIncoming() throws an error.");
do_test_pending();
Svc.Prefs.set("clusterURL", "http://localhost:8080/"); Svc.Prefs.set("clusterURL", "http://localhost:8080/");
Svc.Prefs.set("username", "foo"); Svc.Prefs.set("username", "foo");
@ -126,16 +126,15 @@ function test_processIncoming_error_orderChildren() {
} finally { } finally {
store.wipe(); store.wipe();
server.stop(do_test_finished);
Svc.Prefs.resetBranch(""); Svc.Prefs.resetBranch("");
Records.clearCache(); Records.clearCache();
syncTesting = new SyncTestingInfrastructure(makeEngine); syncTesting = new SyncTestingInfrastructure(makeEngine);
server.stop(run_next_test);
} }
} });
function test_restorePromptsReupload() { add_test(function test_restorePromptsReupload() {
_("Ensure that restoring from a backup will reupload all records."); _("Ensure that restoring from a backup will reupload all records.");
do_test_pending();
Svc.Prefs.set("username", "foo"); Svc.Prefs.set("username", "foo");
Service.serverURL = "http://localhost:8080/"; Service.serverURL = "http://localhost:8080/";
Service.clusterURL = "http://localhost:8080/"; Service.clusterURL = "http://localhost:8080/";
@ -266,15 +265,15 @@ function test_restorePromptsReupload() {
} finally { } finally {
store.wipe(); store.wipe();
server.stop(do_test_finished);
Svc.Prefs.resetBranch(""); Svc.Prefs.resetBranch("");
Records.clearCache(); Records.clearCache();
syncTesting = new SyncTestingInfrastructure(makeEngine); syncTesting = new SyncTestingInfrastructure(makeEngine);
server.stop(run_next_test);
} }
} });
// Bug 632287. // Bug 632287.
function test_mismatched_types() { add_test(function test_mismatched_types() {
_("Ensure that handling a record that changes type causes deletion " + _("Ensure that handling a record that changes type causes deletion " +
"then re-adding."); "then re-adding.");
@ -309,7 +308,6 @@ function test_mismatched_types() {
"parentid": "toolbar" "parentid": "toolbar"
}; };
do_test_pending();
Svc.Prefs.set("username", "foo"); Svc.Prefs.set("username", "foo");
Service.serverURL = "http://localhost:8080/"; Service.serverURL = "http://localhost:8080/";
Service.clusterURL = "http://localhost:8080/"; Service.clusterURL = "http://localhost:8080/";
@ -351,12 +349,12 @@ function test_mismatched_types() {
} finally { } finally {
store.wipe(); store.wipe();
server.stop(do_test_finished);
Svc.Prefs.resetBranch(""); Svc.Prefs.resetBranch("");
Records.clearCache(); Records.clearCache();
syncTesting = new SyncTestingInfrastructure(makeEngine); syncTesting = new SyncTestingInfrastructure(makeEngine);
server.stop(run_next_test);
} }
} });
function run_test() { function run_test() {
initTestLogging("Trace"); initTestLogging("Trace");
@ -364,8 +362,5 @@ function run_test() {
CollectionKeys.generateNewKeys(); CollectionKeys.generateNewKeys();
test_processIncoming_error_orderChildren(); run_next_test();
test_ID_caching();
test_mismatched_types();
test_restorePromptsReupload();
} }

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

@ -7,9 +7,9 @@ Cu.import("resource://services-sync/engines/clients.js");
Cu.import("resource://services-sync/service.js"); Cu.import("resource://services-sync/service.js");
const MORE_THAN_CLIENTS_TTL_REFRESH = 691200; // 8 days const MORE_THAN_CLIENTS_TTL_REFRESH = 691200; // 8 days
const LESS_THAN_CLIENTS_TTL_REFRESH = 86400; // 1 day const LESS_THAN_CLIENTS_TTL_REFRESH = 86400; // 1 day
function test_bad_hmac() { add_test(function test_bad_hmac() {
_("Ensure that Clients engine deletes corrupt records."); _("Ensure that Clients engine deletes corrupt records.");
let global = new ServerWBO('global', let global = new ServerWBO('global',
{engines: {clients: {version: Clients.version, {engines: {clients: {version: Clients.version,
@ -41,7 +41,6 @@ function test_bad_hmac() {
}; };
let server = httpd_setup(handlers); let server = httpd_setup(handlers);
do_test_pending();
try { try {
let passphrase = "abcdeabcdeabcdeabcdeabcdea"; let passphrase = "abcdeabcdeabcdeabcdeabcdea";
@ -133,15 +132,15 @@ function test_bad_hmac() {
do_check_false(oldKey.equals(newKey)); do_check_false(oldKey.equals(newKey));
} finally { } finally {
server.stop(do_test_finished);
Svc.Prefs.resetBranch(""); Svc.Prefs.resetBranch("");
Records.clearCache(); Records.clearCache();
server.stop(run_next_test);
} }
} });
function test_properties() { add_test(function test_properties() {
_("Test lastRecordUpload property");
try { try {
_("Test lastRecordUpload property");
do_check_eq(Svc.Prefs.get("clients.lastRecordUpload"), undefined); do_check_eq(Svc.Prefs.get("clients.lastRecordUpload"), undefined);
do_check_eq(Clients.lastRecordUpload, 0); do_check_eq(Clients.lastRecordUpload, 0);
@ -150,10 +149,11 @@ function test_properties() {
do_check_eq(Clients.lastRecordUpload, Math.floor(now / 1000)); do_check_eq(Clients.lastRecordUpload, Math.floor(now / 1000));
} finally { } finally {
Svc.Prefs.resetBranch(""); Svc.Prefs.resetBranch("");
run_next_test();
} }
} });
function test_sync() { add_test(function test_sync() {
_("Ensure that Clients engine uploads a new client record once a week."); _("Ensure that Clients engine uploads a new client record once a week.");
Svc.Prefs.set("clusterURL", "http://localhost:8080/"); Svc.Prefs.set("clusterURL", "http://localhost:8080/");
Svc.Prefs.set("username", "foo"); Svc.Prefs.set("username", "foo");
@ -172,8 +172,6 @@ function test_sync() {
server.registerPathHandler( server.registerPathHandler(
"/1.1/foo/storage/clients/" + Clients.localID, clientwbo.handler()); "/1.1/foo/storage/clients/" + Clients.localID, clientwbo.handler());
do_test_pending();
try { try {
_("First sync, client record is uploaded"); _("First sync, client record is uploaded");
@ -203,17 +201,14 @@ function test_sync() {
do_check_eq(Clients.lastRecordUpload, yesterday); do_check_eq(Clients.lastRecordUpload, yesterday);
} finally { } finally {
server.stop(do_test_finished);
Svc.Prefs.resetBranch(""); Svc.Prefs.resetBranch("");
Records.clearCache(); Records.clearCache();
server.stop(run_next_test);
} }
} });
function run_test() { function run_test() {
initTestLogging("Trace"); initTestLogging("Trace");
Log4Moz.repository.getLogger("Engine.Clients").level = Log4Moz.Level.Trace; Log4Moz.repository.getLogger("Engine.Clients").level = Log4Moz.Level.Trace;
test_bad_hmac(); run_next_test();
test_properties();
test_sync();
} }

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

@ -551,14 +551,23 @@ var gViewController = {
}, },
loadView: function(aViewId) { loadView: function(aViewId) {
if (aViewId == this.currentViewId) var isRefresh = false;
return; if (aViewId == this.currentViewId) {
if (this.isLoading)
return;
if (!("refresh" in this.currentViewObj))
return;
if (!this.currentViewObj.canRefresh())
return;
isRefresh = true;
}
var state = { var state = {
view: aViewId, view: aViewId,
previousView: this.currentViewId previousView: this.currentViewId
}; };
gHistory.pushState(state); if (!isRefresh)
gHistory.pushState(state);
this.loadViewInternal(aViewId, this.currentViewId, state); this.loadViewInternal(aViewId, this.currentViewId, state);
}, },
@ -598,7 +607,7 @@ var gViewController = {
if (!viewObj.node) if (!viewObj.node)
throw new Error("Root node doesn't exist for '" + view.type + "' view"); throw new Error("Root node doesn't exist for '" + view.type + "' view");
if (this.currentViewObj) { if (this.currentViewObj && aViewId != aPreviousView) {
try { try {
let canHide = this.currentViewObj.hide(); let canHide = this.currentViewObj.hide();
if (canHide === false) if (canHide === false)
@ -617,7 +626,11 @@ var gViewController = {
this.viewPort.selectedPanel = this.currentViewObj.node; this.viewPort.selectedPanel = this.currentViewObj.node;
this.viewPort.selectedPanel.setAttribute("loading", "true"); this.viewPort.selectedPanel.setAttribute("loading", "true");
this.currentViewObj.show(view.param, ++this.currentViewRequest, aState);
if (aViewId == aPreviousView)
this.currentViewObj.refresh(view.param, ++this.currentViewRequest, aState);
else
this.currentViewObj.show(view.param, ++this.currentViewRequest, aState);
}, },
// Moves back in the document history and removes the current history entry // Moves back in the document history and removes the current history entry
@ -1655,7 +1668,7 @@ var gDiscoverView = {
Ci.nsIWebProgress.NOTIFY_STATE_ALL); Ci.nsIWebProgress.NOTIFY_STATE_ALL);
if (self.loaded) if (self.loaded)
self._loadURL(self.homepageURL.spec, notifyInitialized); self._loadURL(self.homepageURL.spec, false, notifyInitialized);
else else
notifyInitialized(); notifyInitialized();
} }
@ -1689,7 +1702,7 @@ var gDiscoverView = {
}); });
}, },
show: function(aParam, aRequest, aState) { show: function(aParam, aRequest, aState, aIsRefresh) {
gViewController.updateCommands(); gViewController.updateCommands();
// If we're being told to load a specific URL then just do that // If we're being told to load a specific URL then just do that
@ -1698,9 +1711,11 @@ var gDiscoverView = {
this._loadURL(aState.url); this._loadURL(aState.url);
} }
// If the view has loaded before and the error page is not visible then // If the view has loaded before and still at the homepage (if refreshing),
// there is nothing else to do // and the error page is not visible then there is nothing else to do
if (this.loaded && this.node.selectedPanel != this._error) { if (this.loaded && this.node.selectedPanel != this._error &&
(!aIsRefresh || (this._browser.currentURI &&
this._browser.currentURI.spec == this._browser.homePage))) {
gViewController.notifyViewChanged(); gViewController.notifyViewChanged();
return; return;
} }
@ -1714,9 +1729,20 @@ var gDiscoverView = {
return; return;
} }
this._loadURL(this.homepageURL.spec, this._loadURL(this.homepageURL.spec, aIsRefresh,
gViewController.notifyViewChanged.bind(gViewController)); gViewController.notifyViewChanged.bind(gViewController));
}, },
canRefresh: function() {
if (this._browser.currentURI &&
this._browser.currentURI.spec == this._browser.homePage)
return false;
return true;
},
refresh: function(aParam, aRequest, aState) {
this.show(aParam, aRequest, aState, true);
},
hide: function() { }, hide: function() { },
@ -1724,7 +1750,7 @@ var gDiscoverView = {
this.node.selectedPanel = this._error; this.node.selectedPanel = this._error;
}, },
_loadURL: function(aURL, aCallback) { _loadURL: function(aURL, aKeepHistory, aCallback) {
if (this._browser.currentURI.spec == aURL) { if (this._browser.currentURI.spec == aURL) {
if (aCallback) if (aCallback)
aCallback(); aCallback();
@ -1734,8 +1760,11 @@ var gDiscoverView = {
if (aCallback) if (aCallback)
this._loadListeners.push(aCallback); this._loadListeners.push(aCallback);
this._browser.loadURIWithFlags(aURL, var flags = 0;
Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY); if (!aKeepHistory)
flags |= Ci.nsIWebNavigation.LOAD_FLAGS_REPLACE_HISTORY;
this._browser.loadURIWithFlags(aURL, flags);
}, },
onLocationChange: function(aWebProgress, aRequest, aLocation) { onLocationChange: function(aWebProgress, aRequest, aLocation) {

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

@ -844,3 +844,63 @@ add_test(function() {
}); });
}, true); }, true);
}); });
// Tests that refreshing the disicovery pane integrates properly with history
add_test(function() {
open_manager("addons://list/plugin", function(aManager) {
is_in_list(aManager, "addons://list/plugin", false, false);
EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("category-discover"), { }, aManager);
wait_for_view_load(aManager, function(aManager) {
is_in_discovery(aManager, MAIN_URL, true, false);
clickLink(aManager, "link-good", function() {
is_in_discovery(aManager, SECOND_URL, true, false);
EventUtils.synthesizeMouseAtCenter(aManager.document.getElementById("category-discover"), { }, aManager);
waitForLoad(aManager, function() {
is_in_discovery(aManager, MAIN_URL, true, false);
go_back(aManager);
waitForLoad(aManager, function() {
is_in_discovery(aManager, SECOND_URL, true, true);
go_back(aManager);
waitForLoad(aManager, function() {
is_in_discovery(aManager, MAIN_URL, true, true);
go_back(aManager);
wait_for_view_load(aManager, function(aManager) {
is_in_list(aManager, "addons://list/plugin", false, true);
go_forward(aManager);
wait_for_view_load(aManager, function(aManager) {
is_in_discovery(aManager, MAIN_URL, true, true);
waitForLoad(aManager, function() {
is_in_discovery(aManager, SECOND_URL, true, true);
waitForLoad(aManager, function() {
is_in_discovery(aManager, MAIN_URL, true, false);
close_manager(aManager, run_next_test);
});
go_forward(aManager);
});
go_forward(aManager);
});
});
});
});
});
});
});
});
});

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

@ -410,7 +410,7 @@ add_test(function() {
gCategoryUtilities.openType("extension", function() { gCategoryUtilities.openType("extension", function() {
gCategoryUtilities.openType("discover", function() { gCategoryUtilities.openType("discover", function() {
is(getURL(browser), "https://example.com/" + RELATIVE_DIR + "releaseNotes.xhtml", "Should have loaded the right url"); is(getURL(browser), MAIN_URL, "Should have loaded the right url");
close_manager(gManagerWindow, run_next_test); close_manager(gManagerWindow, run_next_test);
}); });

189
toolkit/xre/EventTracer.cpp Normal file
Просмотреть файл

@ -0,0 +1,189 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ted Mielczarek <ted.mielczarek@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* Event loop instrumentation. This code attempts to measure the
* latency of the UI-thread event loop by firing native events at it from
* a background thread, and measuring how long it takes for them
* to be serviced. The measurement interval (kMeasureInterval, below)
* is also used as the upper bound of acceptable response time.
* When an event takes longer than that interval to be serviced,
* a sample will be written to the log.
*
* Usage:
*
* Set MOZ_INSTRUMENT_EVENT_LOOP=1 in the environment to enable
* this instrumentation. Currently only the UI process is instrumented.
*
* Set MOZ_INSTRUMENT_EVENT_LOOP_OUTPUT in the environment to a
* file path to contain the log output, the default is to log to stdout.
*
* All logged output lines start with MOZ_EVENT_TRACE. All timestamps
* output are milliseconds since the epoch (PRTime / 1000).
*
* On startup, a line of the form:
* MOZ_EVENT_TRACE start <timestamp>
* will be output.
*
* On shutdown, a line of the form:
* MOZ_EVENT_TRACE stop <timestamp>
* will be output.
*
* When an event servicing time exceeds the threshold, a line of the form:
* MOZ_EVENT_TRACE sample <timestamp> <duration>
* will be output, where <duration> is the number of milliseconds that
* it took for the event to be serviced.
*/
#include "EventTracer.h"
#include <stdio.h>
#include "mozilla/TimeStamp.h"
#include "mozilla/WidgetTraceEvent.h"
#include <prenv.h>
#include <prinrval.h>
#include <prthread.h>
#include <prtime.h>
using mozilla::TimeDuration;
using mozilla::TimeStamp;
using mozilla::FireAndWaitForTracerEvent;
namespace {
PRThread* sTracerThread = NULL;
bool sExit = false;
/*
* The tracer thread fires events at the native event loop roughly
* every kMeasureInterval. It will sleep to attempt not to send them
* more quickly, but if the response time is longer than kMeasureInterval
* it will not send another event until the previous response is received.
*
* The output defaults to stdout, but can be redirected to a file by
* settting the environment variable MOZ_INSTRUMENT_EVENT_LOOP_OUTPUT
* to the name of a file to use.
*/
void TracerThread(void *arg)
{
// This should be set to the maximum latency we'd like to allow
// for responsiveness.
const PRIntervalTime kMeasureInterval = PR_MillisecondsToInterval(50);
FILE* log = NULL;
char* envfile = PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP_OUTPUT");
if (envfile) {
log = fopen(envfile, "w");
}
if (log == NULL)
log = stdout;
fprintf(log, "MOZ_EVENT_TRACE start %llu\n", PR_Now() / PR_USEC_PER_MSEC);
while (!sExit) {
TimeStamp start(TimeStamp::Now());
PRIntervalTime next_sleep = kMeasureInterval;
//TODO: only wait up to a maximum of kMeasureInterval, return
// early if that threshold is exceeded and dump a stack trace
// or do something else useful.
if (FireAndWaitForTracerEvent()) {
TimeDuration duration = TimeStamp::Now() - start;
// Only report samples that exceed our measurement interval.
if (duration.ToMilliseconds() > kMeasureInterval) {
fprintf(log, "MOZ_EVENT_TRACE sample %llu %d\n",
PR_Now() / PR_USEC_PER_MSEC,
int(duration.ToSecondsSigDigits() * 1000));
}
if (next_sleep > duration.ToMilliseconds()) {
next_sleep -= int(duration.ToMilliseconds());
}
else {
// Don't sleep at all if this event took longer than the measure
// interval to deliver.
next_sleep = 0;
}
}
if (next_sleep != 0 && !sExit) {
PR_Sleep(next_sleep);
}
}
fprintf(log, "MOZ_EVENT_TRACE stop %llu\n", PR_Now() / PR_USEC_PER_MSEC);
if (log != stdout)
fclose(log);
}
} // namespace
namespace mozilla {
bool InitEventTracing()
{
// Initialize the widget backend.
if (!InitWidgetTracing())
return false;
// Create a thread that will fire events back at the
// main thread to measure responsiveness.
NS_ABORT_IF_FALSE(!sTracerThread, "Event tracing already initialized!");
sTracerThread = PR_CreateThread(PR_USER_THREAD,
TracerThread,
NULL,
PR_PRIORITY_NORMAL,
PR_GLOBAL_THREAD,
PR_JOINABLE_THREAD,
0);
return sTracerThread != NULL;
}
void ShutdownEventTracing()
{
sExit = true;
if (sTracerThread)
PR_JoinThread(sTracerThread);
sTracerThread = NULL;
// Allow the widget backend to clean up.
CleanUpWidgetTracing();
}
} // namespace mozilla

54
toolkit/xre/EventTracer.h Normal file
Просмотреть файл

@ -0,0 +1,54 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ted Mielczarek <ted.mielczarek@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef XRE_EVENTTRACER_H_
#define XRE_EVENTTRACER_H_
namespace mozilla {
// Create a thread that will fire events back at the
// main thread to measure responsiveness. Return true
// if the thread was created successfully.
bool InitEventTracing();
// Signal the background thread to stop, and join it.
// Must be called from the same thread that called InitEventTracing.
void ShutdownEventTracing();
} // namespace mozilla
#endif /* XRE_EVENTTRACER_H_ */

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

@ -88,6 +88,8 @@ private:
static PRBool sProcessedGetURLEvent = PR_FALSE; static PRBool sProcessedGetURLEvent = PR_FALSE;
@class GeckoNSApplication;
// Methods that can be called from non-Objective-C code. // Methods that can be called from non-Objective-C code.
// This is needed, on relaunch, to force the OS to use the "Cocoa Dock API" // This is needed, on relaunch, to force the OS to use the "Cocoa Dock API"
@ -97,7 +99,7 @@ EnsureUseCocoaDockAPI()
{ {
NS_OBJC_BEGIN_TRY_ABORT_BLOCK; NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
[NSApplication sharedApplication]; [GeckoNSApplication sharedApplication];
NS_OBJC_END_TRY_ABORT_BLOCK; NS_OBJC_END_TRY_ABORT_BLOCK;
} }
@ -112,7 +114,7 @@ SetupMacApplicationDelegate()
AutoAutoreleasePool pool; AutoAutoreleasePool pool;
// Ensure that ProcessPendingGetURLAppleEvents() doesn't regress bug 377166. // Ensure that ProcessPendingGetURLAppleEvents() doesn't regress bug 377166.
[NSApplication sharedApplication]; [GeckoNSApplication sharedApplication];
// This call makes it so that application:openFile: doesn't get bogus calls // This call makes it so that application:openFile: doesn't get bogus calls
// from Cocoa doing its own parsing of the argument string. And yes, we need // from Cocoa doing its own parsing of the argument string. And yes, we need

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

@ -66,6 +66,10 @@ CPPSRCS = \
nsSigHandlers.cpp \ nsSigHandlers.cpp \
$(NULL) $(NULL)
ifdef MOZ_INSTRUMENT_EVENT_LOOP
CPPSRCS += EventTracer.cpp
endif
ifdef MOZ_SPLASHSCREEN ifdef MOZ_SPLASHSCREEN
ifeq ($(OS_ARCH),WINCE) ifeq ($(OS_ARCH),WINCE)
CPPSRCS += nsSplashScreenWin.cpp CPPSRCS += nsSplashScreenWin.cpp

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

@ -62,6 +62,10 @@ using mozilla::dom::ContentParent;
#include "nsAppRunner.h" #include "nsAppRunner.h"
#include "nsUpdateDriver.h" #include "nsUpdateDriver.h"
#ifdef MOZ_INSTRUMENT_EVENT_LOOP
#include "EventTracer.h"
#endif
#ifdef XP_MACOSX #ifdef XP_MACOSX
#include "MacLaunchHelper.h" #include "MacLaunchHelper.h"
#include "MacApplicationDelegate.h" #include "MacApplicationDelegate.h"
@ -3744,6 +3748,13 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
nativeApp->Enable(); nativeApp->Enable();
} }
#ifdef MOZ_INSTRUMENT_EVENT_LOOP
bool event_tracing_running = false;
if (PR_GetEnv("MOZ_INSTRUMENT_EVENT_LOOP")) {
event_tracing_running = mozilla::InitEventTracing();
}
#endif /* MOZ_INSTRUMENT_EVENT_LOOP */
NS_TIME_FUNCTION_MARK("Next: Run"); NS_TIME_FUNCTION_MARK("Next: Run");
NS_TIME_FUNCTION_MARK("appStartup->Run"); NS_TIME_FUNCTION_MARK("appStartup->Run");
@ -3763,6 +3774,11 @@ XRE_main(int argc, char* argv[], const nsXREAppData* aAppData)
NS_TIME_FUNCTION_MARK("appStartup->Run done"); NS_TIME_FUNCTION_MARK("appStartup->Run done");
#ifdef MOZ_INSTRUMENT_EVENT_LOOP
if (event_tracing_running)
mozilla::ShutdownEventTracing();
#endif
// Check for an application initiated restart. This is one that // Check for an application initiated restart. This is one that
// corresponds to nsIAppStartup.quit(eRestart) // corresponds to nsIAppStartup.quit(eRestart)
if (rv == NS_SUCCESS_RESTART_APP) if (rv == NS_SUCCESS_RESTART_APP)

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

@ -52,6 +52,12 @@ EXPORTS_IPC = \
nsGUIEventIPC.h \ nsGUIEventIPC.h \
$(NULL) $(NULL)
ifdef MOZ_INSTRUMENT_EVENT_LOOP
EXPORTS_NAMESPACES += mozilla
EXPORTS_mozilla = \
WidgetTraceEvent.h
endif
EXPORTS = \ EXPORTS = \
nsIWidget.h \ nsIWidget.h \
nsGUIEvent.h \ nsGUIEvent.h \

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

@ -0,0 +1,60 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ted Mielczarek <ted.mielczarek@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#ifndef WIDGET_PUBLIC_WIDGETTRACEEVENT_H_
#define WIDGET_PUBLIC_WIDGETTRACEEVENT_H_
namespace mozilla {
// Perform any required initialization in the widget backend for
// event tracing. Return true if initialization was successful.
bool InitWidgetTracing();
// Perform any required cleanup in the widget backend for event tracing.
void CleanUpWidgetTracing();
// Fire a tracer event at the UI-thread event loop, and block until
// the event is processed. This should only be called by
// a thread that's not the UI thread.
bool FireAndWaitForTracerEvent();
// Signal that the event has been received by the event loop.
void SignalTracerThread();
}
#endif // WIDGET_PUBLIC_WIDGETTRACEEVENT_H_

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

@ -1826,7 +1826,8 @@ inline PRBool NS_IsEventUsingCoordinates(nsEvent* aEvent)
inline PRBool NS_IsEventTargetedAtFocusedWindow(nsEvent* aEvent) inline PRBool NS_IsEventTargetedAtFocusedWindow(nsEvent* aEvent)
{ {
return NS_IS_KEY_EVENT(aEvent) || NS_IS_IME_RELATED_EVENT(aEvent) || return NS_IS_KEY_EVENT(aEvent) || NS_IS_IME_RELATED_EVENT(aEvent) ||
NS_IS_CONTEXT_MENU_KEY(aEvent) || NS_IS_CONTENT_COMMAND_EVENT(aEvent); NS_IS_CONTEXT_MENU_KEY(aEvent) ||
NS_IS_CONTENT_COMMAND_EVENT(aEvent) || NS_IS_PLUGIN_EVENT(aEvent);
} }
/** /**

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

@ -0,0 +1,51 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ted Mielczarek <ted.mielczarek@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
/*
* This file defines constants to be used in the "subtype" field of
* NSApplicationDefined type NSEvents.
*/
#ifndef WIDGET_COCOA_CUSTOMCOCOAEVENTS_H_
#define WIDGET_COCOA_CUSTOMCOCOAEVENTS_H_
// Empty event, just used for prodding the event loop into responding.
const short kEventSubtypeNone = 0;
// Tracer event, used for timing the event loop responsiveness.
const short kEventSubtypeTrace = 1;
#endif /* WIDGET_COCOA_CUSTOMCOCOAEVENTS_H_ */

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

@ -60,40 +60,41 @@ EXPORTS = \
$(NULL) $(NULL)
CMMSRCS = \ CMMSRCS = \
nsBidiKeyboard.mm \ nsBidiKeyboard.mm \
nsClipboard.mm \ nsClipboard.mm \
nsMenuX.mm \ nsMenuX.mm \
nsMenuBarX.mm \ nsMenuBarX.mm \
nsMenuItemX.mm \ nsMenuItemX.mm \
nsMenuItemIconX.mm \ nsMenuItemIconX.mm \
nsMenuUtilsX.mm \ nsMenuUtilsX.mm \
nsMenuGroupOwnerX.mm \ nsMenuGroupOwnerX.mm \
nsFilePicker.mm \ nsFilePicker.mm \
nsDragService.mm \ nsDragService.mm \
nsToolkit.mm \ nsToolkit.mm \
nsAppShell.mm \ nsAppShell.mm \
nsCocoaUtils.mm \ nsCocoaUtils.mm \
nsCocoaWindow.mm \ nsCocoaWindow.mm \
nsChildView.mm \ nsChildView.mm \
nsWindowMap.mm \ nsWindowMap.mm \
nsWidgetFactory.mm \ nsWidgetFactory.mm \
nsCursorManager.mm \ nsCursorManager.mm \
nsMacCursor.mm \ nsMacCursor.mm \
nsScreenCocoa.mm \ nsScreenCocoa.mm \
nsScreenManagerCocoa.mm \ nsScreenManagerCocoa.mm \
nsSound.mm \ nsSound.mm \
nsLookAndFeel.mm \ nsLookAndFeel.mm \
nsNativeThemeCocoa.mm \ nsNativeThemeCocoa.mm \
nsDeviceContextSpecX.mm \ nsDeviceContextSpecX.mm \
nsPrintDialogX.mm \ nsPrintDialogX.mm \
nsPrintOptionsX.mm \ nsPrintOptionsX.mm \
nsPrintSettingsX.mm \ nsPrintSettingsX.mm \
nsIdleServiceX.mm \ nsIdleServiceX.mm \
nsCocoaTextInputHandler.mm \ nsCocoaTextInputHandler.mm \
nsMacDockSupport.mm \ nsMacDockSupport.mm \
nsStandaloneNativeMenu.mm \ nsStandaloneNativeMenu.mm \
GfxInfo.mm \ GfxInfo.mm \
$(NULL) WidgetTraceEvent.mm \
$(NULL)
ifeq (x86_64,$(TARGET_CPU)) ifeq (x86_64,$(TARGET_CPU))
CMMSRCS += ComplexTextInputPanel.mm CMMSRCS += ComplexTextInputPanel.mm

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

@ -0,0 +1,110 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ted Mielczarek <ted.mielczarek@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include <Cocoa/Cocoa.h>
#include "CustomCocoaEvents.h"
#include <Foundation/NSAutoreleasePool.h>
#include <mozilla/CondVar.h>
#include <mozilla/Mutex.h>
#include "mozilla/WidgetTraceEvent.h"
using mozilla::CondVar;
using mozilla::Mutex;
using mozilla::MutexAutoLock;
namespace {
Mutex* sMutex = NULL;
CondVar* sCondVar = NULL;
bool sTracerProcessed = false;
}
namespace mozilla {
bool InitWidgetTracing()
{
sMutex = new Mutex("Event tracer thread mutex");
sCondVar = new CondVar(*sMutex, "Event tracer thread condvar");
return sMutex && sCondVar;
}
void CleanUpWidgetTracing()
{
delete sMutex;
delete sCondVar;
sMutex = NULL;
sCondVar = NULL;
}
// This function is called from the main (UI) thread.
void SignalTracerThread()
{
MutexAutoLock lock(*sMutex);
NS_ABORT_IF_FALSE(!sTracerProcessed, "Tracer synchronization state is wrong");
sTracerProcessed = true;
sCondVar->Notify();
}
// This function is called from the background tracer thread.
bool FireAndWaitForTracerEvent()
{
NS_ABORT_IF_FALSE(sMutex && sCondVar, "Tracing not initialized!");
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
MutexAutoLock lock(*sMutex);
NS_ABORT_IF_FALSE(!sTracerProcessed, "Tracer synchronization state is wrong");
// Post an application-defined event to the main thread's event queue
// and wait for it to get processed.
[NSApp postEvent:[NSEvent otherEventWithType:NSApplicationDefined
location:NSMakePoint(0,0)
modifierFlags:0
timestamp:0
windowNumber:0
context:NULL
subtype:kEventSubtypeTrace
data1:0
data2:0]
atStart:NO];
while (!sTracerProcessed)
sCondVar->Wait();
sTracerProcessed = false;
[pool release];
return true;
}
} // namespace mozilla

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

@ -43,6 +43,8 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#include <dlfcn.h> #include <dlfcn.h>
#include "CustomCocoaEvents.h"
#include "mozilla/WidgetTraceEvent.h"
#include "nsAppShell.h" #include "nsAppShell.h"
#include "nsCOMPtr.h" #include "nsCOMPtr.h"
#include "nsIFile.h" #include "nsIFile.h"
@ -169,6 +171,27 @@ PRBool nsCocoaAppModalWindowList::GeckoModalAboveCocoaModal()
return (topItem.mWidget != nsnull); return (topItem.mWidget != nsnull);
} }
// GeckoNSApplication
//
// Subclass of NSApplication for filtering out certain events.
@interface GeckoNSApplication : NSApplication
{
}
@end
@implementation GeckoNSApplication
- (void)sendEvent:(NSEvent *)anEvent
{
if ([anEvent type] == NSApplicationDefined &&
[anEvent subtype] == kEventSubtypeTrace) {
mozilla::SignalTracerThread();
return;
}
[super sendEvent:anEvent];
}
@end
// AppShellDelegate // AppShellDelegate
// //
// Cocoa bridge class. An object of this class is registered to receive // Cocoa bridge class. An object of this class is registered to receive
@ -286,7 +309,7 @@ nsAppShell::Init()
[NSBundle loadNibFile: [NSBundle loadNibFile:
[NSString stringWithUTF8String:(const char*)nibPath.get()] [NSString stringWithUTF8String:(const char*)nibPath.get()]
externalNameTable: externalNameTable:
[NSDictionary dictionaryWithObject:[NSApplication sharedApplication] [NSDictionary dictionaryWithObject:[GeckoNSApplication sharedApplication]
forKey:@"NSOwner"] forKey:@"NSOwner"]
withZone:NSDefaultMallocZone()]; withZone:NSDefaultMallocZone()];
@ -388,7 +411,7 @@ nsAppShell::ProcessGeckoEvents(void* aInfo)
timestamp:0 timestamp:0
windowNumber:0 windowNumber:0
context:NULL context:NULL
subtype:0 subtype:kEventSubtypeNone
data1:0 data1:0
data2:0] data2:0]
atStart:NO]; atStart:NO];
@ -410,7 +433,7 @@ nsAppShell::ProcessGeckoEvents(void* aInfo)
timestamp:0 timestamp:0
windowNumber:0 windowNumber:0
context:NULL context:NULL
subtype:0 subtype:kEventSubtypeNone
data1:0 data1:0
data2:0] data2:0]
atStart:NO]; atStart:NO];

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

@ -5649,6 +5649,15 @@ static const char* ToEscapedString(NSString* aString, nsCAutoString& aBuf)
return; return;
} }
} }
// Don't send complex text input to a plugin in Cocoa event mode if
// either the Control key or the Command key is pressed -- even if the
// plugin has requested it, or we are already in IME composition. This
// conforms to our behavior in 64-bit mode and fixes bug 619217.
NSUInteger modifierFlags = [theEvent modifierFlags];
if ((modifierFlags & NSControlKeyMask) || (modifierFlags & NSCommandKeyMask)) {
return;
}
} }
// This will take care of all Carbon plugin events and also send Cocoa plugin // This will take care of all Carbon plugin events and also send Cocoa plugin

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

@ -62,23 +62,24 @@ ifdef ACCESSIBILITY
CSRCS += maiRedundantObjectFactory.c CSRCS += maiRedundantObjectFactory.c
endif endif
CPPSRCS = \ CPPSRCS = \
nsWindow.cpp \ nsWindow.cpp \
nsAppShell.cpp \ nsAppShell.cpp \
nsWidgetFactory.cpp \ nsWidgetFactory.cpp \
nsToolkit.cpp \ nsToolkit.cpp \
nsBidiKeyboard.cpp \ nsBidiKeyboard.cpp \
nsLookAndFeel.cpp \ nsLookAndFeel.cpp \
nsGtkKeyUtils.cpp \ nsGtkKeyUtils.cpp \
nsFilePicker.cpp \ nsFilePicker.cpp \
nsSound.cpp \ nsSound.cpp \
nsNativeKeyBindings.cpp \ nsNativeKeyBindings.cpp \
nsScreenGtk.cpp \ nsScreenGtk.cpp \
nsScreenManagerGtk.cpp \ nsScreenManagerGtk.cpp \
nsImageToPixbuf.cpp \ nsImageToPixbuf.cpp \
nsAccessibilityHelper.cpp \ nsAccessibilityHelper.cpp \
nsGtkIMModule.cpp \ nsGtkIMModule.cpp \
$(NULL) WidgetTraceEvent.cpp \
$(NULL)
ifdef MOZ_X11 ifdef MOZ_X11
CPPSRCS += nsIdleServiceGTK.cpp CPPSRCS += nsIdleServiceGTK.cpp

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

@ -0,0 +1,103 @@
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* The Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2011
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Ted Mielczarek <ted.mielczarek@gmail.com>
*
* Alternatively, the contents of this file may be used under the terms of
* either the GNU General Public License Version 2 or later (the "GPL"), or
* the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "mozilla/WidgetTraceEvent.h"
#include <glib.h>
#include <mozilla/CondVar.h>
#include <mozilla/Mutex.h>
#include <stdio.h>
using mozilla::CondVar;
using mozilla::Mutex;
using mozilla::MutexAutoLock;
namespace {
Mutex* sMutex = NULL;
CondVar* sCondVar = NULL;
bool sTracerProcessed = false;
// This function is called from the main (UI) thread.
gboolean TracerCallback(gpointer data)
{
MutexAutoLock lock(*sMutex);
NS_ABORT_IF_FALSE(!sTracerProcessed, "Tracer synchronization state is wrong");
sTracerProcessed = true;
sCondVar->Notify();
return FALSE;
}
} // namespace
namespace mozilla {
bool InitWidgetTracing()
{
sMutex = new Mutex("Event tracer thread mutex");
sCondVar = new CondVar(*sMutex, "Event tracer thread condvar");
return sMutex && sCondVar;
}
void CleanUpWidgetTracing()
{
delete sMutex;
delete sCondVar;
sMutex = NULL;
sCondVar = NULL;
}
// This function is called from the background tracer thread.
bool FireAndWaitForTracerEvent()
{
NS_ABORT_IF_FALSE(sMutex && sCondVar, "Tracing not initialized!");
// Send a default-priority idle event through the
// event loop, and wait for it to finish.
MutexAutoLock lock(*sMutex);
NS_ABORT_IF_FALSE(!sTracerProcessed, "Tracer synchronization state is wrong");
g_idle_add_full(G_PRIORITY_DEFAULT,
TracerCallback,
NULL,
NULL);
while (!sTracerProcessed)
sCondVar->Wait();
sTracerProcessed = false;
return true;
}
} // namespace mozilla

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше