/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- * * The contents of this file are subject to the Netscape 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/NPL/ * * 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 Communicator client code. * * The Initial Developer of the Original Code is Netscape Communications * Corporation. Portions created by Netscape are * Copyright (C) 1998 Netscape Communications Corporation. All * Rights Reserved. * * Original Author: David W. Hyatt (hyatt@netscape.com) * */ #include "nsCOMPtr.h" #include "nsIXBLPrototypeHandler.h" #include "nsXBLWindowKeyHandler.h" #include "nsIXBLPrototypeBinding.h" #include "nsIContent.h" #include "nsIAtom.h" #include "nsIDOMNSUIEvent.h" #include "nsIDOMKeyEvent.h" #include "nsIDOMEventReceiver.h" #include "nsIPrivateDOMEvent.h" #include "nsXBLService.h" #include "nsIServiceManager.h" #include "nsHTMLAtoms.h" #include "nsINameSpaceManager.h" #include "nsIXBLDocumentInfo.h" #include "nsIScriptGlobalObject.h" #include "nsIDOMWindowInternal.h" #include "nsPIDOMWindow.h" #include "nsIFocusController.h" #include "nsIDocShell.h" #include "nsIPresShell.h" #include "nsIDOMDocument.h" #include "nsIDocument.h" #include "nsIDOMElement.h" #include "nsPIWindowRoot.h" PRUint32 nsXBLWindowKeyHandler::gRefCnt = 0; nsIAtom* nsXBLWindowKeyHandler::kKeyDownAtom = nsnull; nsIAtom* nsXBLWindowKeyHandler::kKeyUpAtom = nsnull; nsIAtom* nsXBLWindowKeyHandler::kKeyPressAtom = nsnull; struct nsXBLSpecialDocInfo { nsCOMPtr mHTMLBindings; nsCOMPtr mPlatformHTMLBindings; PRBool mFilesPresent; nsXBLSpecialDocInfo() :mFilesPresent(PR_TRUE) {}; }; nsXBLWindowKeyHandler::nsXBLWindowKeyHandler(nsIDOMElement* aElement, nsIDOMEventReceiver* aReceiver) { NS_INIT_ISUPPORTS(); mElement = aElement; mReceiver = aReceiver; mXBLSpecialDocInfo = nsnull; gRefCnt++; if (gRefCnt == 1) { kKeyUpAtom = NS_NewAtom("keyup"); kKeyDownAtom = NS_NewAtom("keydown"); kKeyPressAtom = NS_NewAtom("keypress"); } } nsXBLWindowKeyHandler::~nsXBLWindowKeyHandler() { delete mXBLSpecialDocInfo; gRefCnt--; if (gRefCnt == 0) { NS_RELEASE(kKeyUpAtom); NS_RELEASE(kKeyDownAtom); NS_RELEASE(kKeyPressAtom); } } NS_IMPL_ISUPPORTS1(nsXBLWindowKeyHandler, nsIDOMKeyListener) PRBool nsXBLWindowKeyHandler::IsEditor() { nsCOMPtr windowRoot(do_QueryInterface(mReceiver)); nsCOMPtr focusController; windowRoot->GetFocusController(getter_AddRefs(focusController)); if (!focusController) { NS_WARNING("********* Something went wrong! No focus controller on the root!!!\n"); return PR_FALSE; } nsCOMPtr focusedWindow; focusController->GetFocusedWindow(getter_AddRefs(focusedWindow)); if (!focusedWindow) return PR_FALSE; nsCOMPtr obj(do_QueryInterface(focusedWindow)); nsCOMPtr docShell; obj->GetDocShell(getter_AddRefs(docShell)); nsCOMPtr presShell; if (docShell) docShell->GetPresShell(getter_AddRefs(presShell)); if (presShell) { PRBool isEditor; presShell->GetDisplayNonTextSelection(&isEditor); return isEditor; } return PR_FALSE; } static void GetHandlers(nsIXBLDocumentInfo* aInfo, const nsCString& aDocURI, const nsCString& aRef, nsIXBLPrototypeHandler** aResult) { nsCOMPtr binding; aInfo->GetPrototypeBinding(aRef, getter_AddRefs(binding)); if (!binding) { nsCOMPtr doc; aInfo->GetDocument(getter_AddRefs(doc)); nsCOMPtr root = getter_AddRefs(doc->GetRootContent()); PRInt32 childCount; root->ChildCount(childCount); for (PRInt32 i = 0; i < childCount; i++) { nsCOMPtr child; root->ChildAt(i, *getter_AddRefs(child)); nsAutoString id; child->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::id, id); if (id.EqualsWithConversion(aRef)) { NS_NewXBLPrototypeBinding(aRef, child, aInfo, getter_AddRefs(binding)); aInfo->SetPrototypeBinding(aRef, binding); break; } } } nsCOMPtr dummy; binding->GetPrototypeHandlers(aResult, getter_AddRefs(dummy)); // Addref happens here. } NS_IMETHODIMP nsXBLWindowKeyHandler::EnsureHandlers() { if (mElement) { if (mHandler) return NS_OK; // Call into the XBL service. nsCOMPtr content(do_QueryInterface(mElement)); nsCOMPtr dummy; nsXBLService::BuildHandlerChain(content, getter_AddRefs(mHandler), getter_AddRefs(dummy)); } else { if (!mXBLSpecialDocInfo) mXBLSpecialDocInfo = new nsXBLSpecialDocInfo(); if (!mXBLSpecialDocInfo) return NS_ERROR_OUT_OF_MEMORY; if (!mXBLSpecialDocInfo->mFilesPresent) return NS_OK; if (!mXBLSpecialDocInfo->mHTMLBindings || !mXBLSpecialDocInfo->mPlatformHTMLBindings) { nsresult rv; NS_WITH_SERVICE(nsIXBLService, xblService, "@mozilla.org/xbl;1", &rv); if (xblService) { // Obtain the two doc infos we need. xblService->LoadBindingDocumentInfo(nsnull, nsnull, nsCAutoString("chrome://global/content/htmlBindings.xml"), nsCAutoString(""), PR_TRUE, getter_AddRefs(mXBLSpecialDocInfo->mHTMLBindings)); xblService->LoadBindingDocumentInfo(nsnull, nsnull, nsCAutoString("chrome://global/content/platformHTMLBindings.xml"), nsCAutoString(""), PR_TRUE, getter_AddRefs(mXBLSpecialDocInfo->mPlatformHTMLBindings)); if (!mXBLSpecialDocInfo->mHTMLBindings || !mXBLSpecialDocInfo->mPlatformHTMLBindings) { mXBLSpecialDocInfo->mFilesPresent = PR_FALSE; return NS_OK; } } } // Now determine which handlers we should be using. if (IsEditor()) { GetHandlers(mXBLSpecialDocInfo->mPlatformHTMLBindings, nsCAutoString("chrome://global/content/platformHTMLBindings.xml"), nsCAutoString("editor"), getter_AddRefs(mPlatformHandler)); GetHandlers(mXBLSpecialDocInfo->mHTMLBindings, nsCAutoString("chrome://global/content/htmlBindings.xml"), nsCAutoString("editorBase"), getter_AddRefs(mHandler)); } else { GetHandlers(mXBLSpecialDocInfo->mPlatformHTMLBindings, nsCAutoString("chrome://global/content/platformHTMLBindings.xml"), nsCAutoString("browser"), getter_AddRefs(mPlatformHandler)); GetHandlers(mXBLSpecialDocInfo->mHTMLBindings, nsCAutoString("chrome://global/content/htmlBindings.xml"), nsCAutoString("browserBase"), getter_AddRefs(mHandler)); } } return NS_OK; } NS_IMETHODIMP nsXBLWindowKeyHandler::WalkHandlersInternal(nsIDOMKeyEvent* aKeyEvent, nsIAtom* aEventType, nsIXBLPrototypeHandler* aHandler) { nsresult rv; nsCOMPtr currHandler = aHandler; while (currHandler) { PRBool stopped; nsCOMPtr privateEvent(do_QueryInterface(aKeyEvent)); privateEvent->IsDispatchStopped(&stopped); if (stopped) return NS_OK; PRBool matched = PR_FALSE; currHandler->KeyEventMatched(aEventType, aKeyEvent, &matched); if (matched) { // Make sure it's not disabled. nsAutoString disabled; nsCOMPtr elt; currHandler->GetHandlerElement(getter_AddRefs(elt)); /*nsAutoString id; elt->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::id, id); nsCAutoString idc; idc.AssignWithConversion(id); if (!idc.IsEmpty()) printf("Key matched with id of: %s\n", (const char*)idc); */ elt->GetAttribute(kNameSpaceID_None, nsHTMLAtoms::disabled, disabled); if (!disabled.Equals(NS_LITERAL_STRING("true"))) { nsCOMPtr rec = mReceiver; if (mElement) rec = do_QueryInterface(elt); rv = currHandler->ExecuteHandler(rec, aKeyEvent); if (NS_SUCCEEDED(rv)) { return NS_OK; } } } nsCOMPtr nextHandler; currHandler->GetNextHandler(getter_AddRefs(nextHandler)); currHandler = nextHandler; } return NS_OK; } NS_IMETHODIMP nsXBLWindowKeyHandler::WalkHandlers(nsIDOMEvent* aKeyEvent, nsIAtom* aEventType) { nsCOMPtr evt = do_QueryInterface(aKeyEvent); PRBool prevent; evt->GetPreventDefault(&prevent); if (prevent) return NS_OK; nsCOMPtr keyEvent(do_QueryInterface(aKeyEvent)); if (!keyEvent) return NS_OK; EnsureHandlers(); if (!mElement) { WalkHandlersInternal(keyEvent, aEventType, mPlatformHandler); evt->GetPreventDefault(&prevent); if (prevent) return NS_OK; // Handled by the platform. Our work here is done. } WalkHandlersInternal(keyEvent, aEventType, mHandler); return NS_OK; } nsresult nsXBLWindowKeyHandler::KeyUp(nsIDOMEvent* aKeyEvent) { return WalkHandlers(aKeyEvent, kKeyUpAtom); } nsresult nsXBLWindowKeyHandler::KeyDown(nsIDOMEvent* aKeyEvent) { return WalkHandlers(aKeyEvent, kKeyDownAtom); } nsresult nsXBLWindowKeyHandler::KeyPress(nsIDOMEvent* aKeyEvent) { return WalkHandlers(aKeyEvent, kKeyPressAtom); } /////////////////////////////////////////////////////////////////////////////////// nsresult NS_NewXBLWindowKeyHandler(nsIDOMElement* aElement, nsIDOMEventReceiver* aReceiver, nsXBLWindowKeyHandler** aResult) { *aResult = new nsXBLWindowKeyHandler(aElement, aReceiver); if (!*aResult) return NS_ERROR_OUT_OF_MEMORY; NS_ADDREF(*aResult); return NS_OK; }