Gecko Embedding Basics

  • 리비전 슬러그: Gecko_Embedding_Basics
  • 리비전 제목: Gecko Embedding Basics
  • 리비전 아이디: 164308
  • 제작일시:
  • 만든이: Javanese
  • 현재 리비전인가요? 아니오
  • 댓글 /* nsISupports */

리비전 내용

정보의 원천, 놀이, 개인간 교류 수단으로써 웹의 중요성이 끝없이 증가하면서, HTML 포맷으로 저장된 데이터에 접근하고 보는 능력은 다양한 소프트웨어 애플리케이션을 폭넓게 대체하기 위해 점점 더 중요해지고 있다. 단순한 HTML 페이지 뷰어건 완벽한 기능의 웹 브라우저이건, HTML 기반 문서를 해석하고 그려내는 것은 수많은 상황에서 매우 중요한 기능이 됐다. 애플리케이션 개발자는 개발 시간을 최소화하면서도 애자일하고 안정성이 높은 제품을 만들기 위해, HTML을 출력하는 결정적인 기능을 어떻게 구현할지에 고민하게 되었다. Gecko, 즉 넷스케이프와 모질라 브라우저의 핵심에 있는 렌더링 엔진을 임베딩하는 것은 이런 문제에 대한 훌륭한 해결책이다.

왜 Gecko인가?

Gecko를 임베딩하는 것은 현명한 선택이다. Gecko는 빠르고, 안정적이며 표준을 매우 잘 지키고 있다. Mozilla와 Netscape의 지원 아래서 매우 널리 배포되었으며 많은 사람들이 꾸준히 지켜보고 있다.

Gecko는 오픈 소스이다. 다른 임베딩 브라우저 솔루션과는 달리, Gecko의 모든 소스 코드는 자유롭게 사용할 수 있으며 마음껏 수정할 수 있다. 필요한 만큼 뜯어 고칠 수 있다. 또한 현재의 라이센스에 따르면, Gecko를 독점적인 상용 제품에 컴포넌트로 사용할 수도 있다.

Gecko는 Mozilla 프로젝트와 연관되어 있으므로, Mozilla 프로젝트에서 임베딩 작업에 도움이 되는 많은 리소스를 쉽게 얻을 수 있다. Mozilla 웹 사이트에는 임베딩 프로젝트 영역이 있다. 또한 mozilla.dev.emobedding 뉴스그룹이 있으며, 많은 관련 뉴스그룹과 함께 임베딩 개발자들 간에 정보를 교환하는 것을 주 목적으로 하고 있다. 전체 코드기반 상호 참조가 가능하며, Bugzilla 버그 데이터베이스를 통해 버그 제출, 버그 진행 상황 참조, 버그 수정 참여 등을 간단히 할 수 있다.

Gecko는 바닥부터 크로스플랫폼으로 설계되었다. mozill.org에서는 직접 Wintel, MacOS 9.0과 OS X, 리눅스를 지원하고 있으며, 수많은 다른 플랫폼도 써드파티에서 지원하고 있다.

마지막으로, Gecko를 라이센스하는 것은 로열티가 필요 없으며, 심지어 독점 저작권을 갖는 상용 제품에도 로열티없이 적용할 수 있다. "매우" 일반적으로는, 모질라가 제공하는 원본 소스코드에 대해서는 어떤 수정도 커뮤니티에 반환되어야 한다.(Gecko를 임베드하는 코드까지 반환해야하는 것은 아니다.) 즉 애플리케이션의 사용자라면 사용중인 Gecko 엔진에 해당하는 소스 코드를 언제든지 얻을 수 있어야 하며(대부분 mozilla.org 웹사이트 링크를 통해서), 애플리케이션은 명확한 방법으로 Gecko를 사용하고 있음을 공지해야 한다. (예를 들면, 제품 박스 위의 로고나 About: 페이지 등) 가능한 라이센스 방법에 대한 정확한 설명은 라이센스에 대해 유일한 법적 근거인 Mozilla & Nescape Public Licneses에서 볼 수 있다.

임베딩에 필요한 것

Gecko를 임베드하기로 결정했다면, 세가지 과정을 밟아야 한다. 첫째로 코드를 얻는다. 다음으로 Gecko 코드베이스를 다루는데 사용되는 몇가지 지정 기술을 이해해야 한다. 마지막으로 추가하려는 부가적인 기능을 결정해야 한다. 이 절은 이러한 과정을 안내할 것이다.

코드 얻기

이 글을 쓰는 시점에서 Gecko를 임베드하기 위한 파일을 얻는 가장 좋은 방법은 전체 Mozill 소스 트리를 다운로드해서 빌드하는 것이다. 사실 상당히 간단한 과정이다. 전체 설명과 적절한 링크는 Download Mozilla Source Code에서 찾을 수 있다. 두번째 방법(컴포넌트에 의한 컴포넌트)은 현재 개발 중이며, 아직 베타 상태에 있다. 이 프로젝트에 대한 정보는 REQUIRES 기반 빌드 메카니즘에서 찾을 수 있다. 그 외에, Gecko 런타임 환경(GRE, Gecko Runtime Environment)가 개발중에 있다. GRE는 단일 코어 라이브러리를 사용하는 모질라 컴포넌트 위에서 여러가지 애플리케이션을 지원하기 위한 것이다. (컴포넌트에 의한 컴포넌트를 사용하려고 생각한다면, 우선 버전과 바이너리 호환성에 대한 이슈를 잘 살펴봐야 한다. XPCOM 컴포넌트 재사용을 살펴보면 도움이 될 것이다.)

우선 몇가지 도구(기본으로 컴파일러, 펄 배포판, 몇몇 범용 유틸리티)를 가져와야 한다. 그런 다음 컴퓨터 환경을 설정하고, 소스를 다운로드 한다. 전체 트리를 다운로드하려 한다면, 두가지 방법이 있다. FTP에서 전체 소스 트리를 tar로 압축한 파일을 가져올 수 있다. (이것이 가장 간단한 방법이며, 확실히 컴파일이 가능한 한 방법이다. 다만 최근에 수정된 코드는 포함하지 않았을 수도 있다.) 다른 방법은 CVS에서 가장 최신의 코드를 가져오거나 변경분에 대한 업데이트를 수행하는 것이다. 소스를 가져와서 환경 설정을 적절히 끝마친 다음에는 제공된 makfile 중 적절한 것을 실행하면 된다. 각 플랫폼마다 컴파일 과정에 대한 상세한 설명이 있다.

빌드가 끝나면 mozilla/embedding/config 디렉토리로 간다. 거기서 예제 manifest 파일을 찾을 수 있을 것이다. (파일들은 모두 이름이 "basebrowser"로 시작한다. manifest 파일은 각 플래폼마다 임베딩을 위한 개별 설정이다. 기본으로 들어있는 것은 단지 예제일 뿐이며, 당신의 목적에는 딱 들어맞지 않을 수도 있다. 하지만 예제로 시작해서 조금씩 수정해 나가면 좋을 것이다. 각 플랫폼마다 모델로 삼을 만한 예제 임베딩 프로젝트들도 있다. Roll_your_own_browser_-_An_embedding_HowTo를 참고하라.

코드 작성 환경 이해하기

모질라는 처음부터 여러 플랫폼과 프로그래밍 언어에 대한 설계와 개발을 지원하는 것으로 시작됐다. 결국 여러가지 자체 프로그래밍 기술이 개발되었으며, 모두 다 객체 은폐라는 이상적인 개념을 바탕으로 두고 있다. Gecko 임베딩에는 XPCOM, XPIDL, XPConnect, 특별 문자열 클래스, XUL 등의 기술에 대한 실제적인 지식을 필요로 한다. 다음은 각 기술에 대한 간략한 소개이다. 더 자세한 정보는 mozilla.org 사이트에서 찾을 수 있다.

XPCOM

모질라 기술 중 가장 중요한 것은 XPCOM, 즉 크로스플랫폼 컴포넌트 오브젝트 모델(Cross-Platform Component Object Model)이다. XPCOM은 객체나 다른 데이터의 생성, 소유, 제거를 모질라 전체에 걸쳐 관리하는 프레임워크를 제공한다. MSCOM을 사용한 적이 있다면, 기본적으로 유사한 개념임을 느낄 것이다. 하지만 매우 큰 차이가 있다. XPCOM은 크로스플랫폼이며 단일 스레드에서 대부분 동작하도록 설계되어 있다. 또한 XPCOM와 MSCOM은 현재로써는 아직 호환되지 않는다.

인터페이스

XPCOM의 핵심은 인터페이스라는 개념이다. 인터페이스는 특정 기증과 연관된 메소드, 속성, 관련 상수의 집합을 기술한 것일 뿐이다. 실제 기능을 구현하는 클래스와는 전혀 별개이다. 인터페이스는 계약의 역할을 한다. 특정 인터페이스를 지원하는 객체는 인터페이스에 기술된 서비스를 수행한다는 약속의 의미이다. 인터페이스를 최대한 중립적으로 유지할 수 있도록 IDL(Interface Definition Language)라는 별도의 언어를 사용해서 기술한다. 인터페이스 파일은 일반저긍로 .idl이라는 확장자를 갖는다. 인터페이스의 기능을 지정할 뿐만 아니라 인터페이스의 IID도 지정한다. IID는 인터페이스를 지정할 때 사용하는 세계에서 유일한 번호이다.

Gecko 내에서 이루어지는 통신의 대부분은 추상 구조체를 거친다. (규약에 따라 추상 구조체의 이름은 nsISomething의 형식을 따른다.)

//이거야
void ProcessSample(nsISample* aSample) {
	aSample->Poke("Hello"); 
//이건 아니야
void ProcessSample(nsSampleImpl* aSample) {
	aSample->Poke("hello"); 
@status FROZEN

XPCOM의 추상화 수준으로 인해 시스템이 매우 큰 유연성을 갖추게 됐다. 구현은 필요에 따라 얼마든지 변경할 수 있다. 하지만 최소한 인터페이스 자체는 고정된 채로 유지해야 한다. 모질라의 초기 설계와 개발 기간 동안에는 인터페이스는 다소 유동적인 것이었지만, 프로젝트가 성숙되면서 점점 더 많은 인터페이스가 FROZEN으로 표시됐다. FROZEN으로 표시된 인터페이스는 향후 변경되지 않음을 보장한다는 의미이다.

임베딩 관련된 대부분의 주요 인터페이스가 FROZEN으로 표시되긴 했지만, 인터페이스를 사용하기 전에 확인해보는 것이 좋다. 인터페이스의 상태는 .idl 파일의 주석에 나열된다. 얼려진 인터페이스(frozen interface)는 @status FROZEN라는 표시가 달린다. 모질라 상호 참조 도구를 사용해서 얼려진 인터페이스를 검색할 수도 있다. 얼려지지 않은 상태의 인터페이스는 언제든지 변경될 가능성이 있다. 얼리는 과정에 대한 자세한 정보는 임베딩 프로젝트 페이지를 참고하라.

인터페이스가 얼려지고 나면 해당 인터페이스는 Gecko 임베딩 API 참조에 추가된다.

nsISupports

한 객체가 하나 이상의 인터페이스를 지원할 수 있다. 실제로는 모든 객체는 최소한 두 개의 인터페이스를 지원해야 한다. 하나는 실제 객체의 역할이 되는 무언가이고, 다른 하나는 범용적인 목적으로 사용되는 nsISupports이다. 다르게 말하자면 nsISupports는 모든 XPCOM 인터페이스의 조상이다. 모든 인터페이스는 nsISupports로부터 상속되며, 대부분은 직접 상속받는다. nsISupports는 두가지 주요 기능을 갖는다. 런타임 타입 발견과 객체 생명주기 관리이다. MSCOM의 IUnknown과 동일한 역할을 한다.

모든 객체가 다중 인터페이스를 지원하기 때문에, 한 인터페이스에 대한 포인터를 가진 상태에서 해당 객체가 당장 필요한 기능을 제공하는 다른 인터페이스를 지원하는지 알아낼 수 있다. 첫번째 nsISupports 메소드인 QueryInterface()는 다음과 같은 질문을 수행한다. "나는 이 객체가 A 타입임(인터페이스 A를 지원함)을 알고 있다. 그런데 타입 B이기도 한가?(인터페이스 B도 지원하는가?)"

만일 그렇다면 QueryInterface()는 새로 요청된 인터페이스로 연결된 포인터를 반환한다.

void ProcessSample(nsISample* aSample) {
	nsIExample *example;
	nsresult rv;
	rv = aSample->QueryInterface(NS_GET_IID(nsIExample),(void **)&example); 
	if (NS_SUCCEEDED(rv)) {
		example->DoSomeOperation();
		NS_RELEASE(example); // Release호출을 위해 매크로를 사용 
	} 
} 

XPCOM은 간접적인 방법(컴포넌트 관리자)을 사용해서 객체의 인스턴스를 생성하기 때문에, 같은 객체에 대한 다중 포인터(각각 다양한 인터페이스로 연결됨)가 존재할 수 있으며, 포인터가 가리키는 모든 객체를 호출자가 정확하게 추적하기 어려워질 수 있다. 객체가 필요하지 않은 상황에서도 남아있게 되서 메미로 누수가 발생하거나, 필요한 객체가 삭제되는 바람에 댕글링 포인터가 나타날 수도 있다. nsISupports<code>의 다른 두 메소드 <code>AddRef()Release()는 이런 이슈를 다루기 위해 설계된 것이다. 포인터가 변수에 입력될 때마다AddRef()가 호출되어 내부 카운터가 증가한다. 포인터가 풀려날 때마다 Release()가 호출되어 내부 카운터가 감소한다. 카운터가 0이 되면, 객체에 대한 포인터가 없으며 객체를 안전하게 삭제할 수 있다. 객체의 수명에 대한 제어는 객체 내부에 있는 것이다. 이와 같은 과정을 자동화할 수 있도록 돕는 유틸리티인 XPCOM "스마트" 포인터nsCOMPtr에 대한 정보를 참고하라.

Object creation

The instantiation of objects is also an indirect process in XPCOM. Just as interfaces have a globally unique ID number (the IID), XPCOM classes are assigned their own GUIDs, the CID. In addition, they are also often given a text-based ID, called a contract ID. One or the other of these IDs is passed to a method on a persistent XPCOM component, the Component Manager, which actually creates the object. When a new library of classes (called a module in XPCOM) is first introduced into the system, it must register itself with the Component Manager, which maintains a registry that maps classes (with their IDs) to the libraries in which they reside.

A limited number of persistent services, supplied by singleton objects, are created and controlled by a companion to the Component Manager, the Service Manager. The Component Manager itself is an example of such a persistent service.

Summing up

Functionality in XPCOM is described by abstract interfaces, and most communication among parts of the system takes place in terms of those interfaces. The underlying objects that implement the interfaces, on the other hand, are created indirectly by the Component Manager based on a cross-indexed registry that it maintains.

One functionality shared by all interfaces is the ability to query the underlying object at runtime to see if also implements other interfaces. In theory an interface is fixed and unchangeable, but at this stage in the Mozilla codebase, only interfaces that have been declared FROZEN are guaranteed not to change significantly. Object lifetime management takes place inside the object itself through an internal counter that keeps track of the number of pointers to the object that have been added or released. The client's only responsibility is to increment and decrement the counter. When the internal counter reaches zero, the object deletes itself.

nsCOMPtr

Sometimes, however, even remembering to call AddRef() and Release() at the right times can be difficult. To make this process easier and more reliable, XPCOM has a built-in "smart" pointer, nsCOMPtr. This pointer takes care of calling AddRef() and Release() for you. Using nsCOMPtr whenever possible will make your code cleaner and more efficient. For more information on the smart pointer, see "The Complete nsCOMPtr User's Manual".

Mozilla actually provides a large number of built-in macros (by convention, written in all caps in the code) and utilities like nsCOMPtr that can make the entire process of coding with XPCOM easier. Many of these can be found in the following files: <tt>nsCom.h</tt>, <tt>nsDebug.h</tt>, <tt>nsError.h</tt>, <tt>nsIServiceManager.h</tt>, and <tt>nsISupportsUtils.h</tt>. Mozilla also supplies other development tools for tracking memory usage and the like. More information on these can be found at http://www.mozilla.org/performance/

For more information

More information on XPCOM in general can be found at XPCOM. For an overview of creating XPCOM components, see Chapter 8 of O'Reilly's Creating Applications with Mozilla. There is also a new book completely devoted to this topic, Creating XPCOM Components. A fuller explanation of some of the underlying logic to COM systems can be found in the early chapters of Essential COM by Don Box. While it focuses on MSCOM in particular, the book does provide an excellent background on some of the core rationales for using such an object model.

XPIDL

Interfaces are abstract classes written in XPIDL, the Cross Platform Interface Definition Language. Yet to be useful the functionality promised in those interfaces must be implemented in some regular programming language. Facilitating this is the job of the XPIDL compiler. Once an interface is defined in an .idl file, it can be processed by the XPIDL compiler.

The compiler can be set to output a number of things, but generally the output is two-fold: a C++ .h file that includes a commented out template for a full C++ implementation of the interface and an XPT file that contains type library information which works with XPConnect to make the interface available to JavaScript. More information on the syntax of XPIDL (a simple C-like language) and the use of the compiler is available.

XPConnect and XPT files

XPConnect is an XPCOM module that allows code written in JavaScript to access and manipulate XPCOM components written in C++ and vice versa. By means of XPConnect, components on either side of an XPCOM interface do not, in general, need to know or care about which of these languages the object on the other side is implemented in.

When an interface is run through the XPIDL compiler, it produces an XPT or type library file. Because XPconnect uses the information in this file to implement transparent communication between C++ objects and JavaScript objects across XPCOM interfaces, it is important to make sure they are generated and included with your code even if you are developing exclusively in C++. Not only is a substantial part of the browser, in fact, implemented in JS, it is possible that in the future someone may wish to use JS-based code to interact with whatever components you create .

As is from Mozilla, XPConnect currently facilitates interoperability between C++ and JS. Modules to extend it to allow access from other languages (including Python) are under independent development.

String classes

Web browsing typically involves a large amount of string manipulation. Mozilla has developed a hierarchy of C++ classes to facilitate such manipulation and to render it efficient and quick. To make communication among objects simpler and more error free, Mozilla uses interfaces, which are, in essence, abstract classes. The string hierarchy is also headed up by a set of abstract classes, nsAString, nsASingleFragmentString, and nsAFlatString, and for the same reasons. (These refer to double-byte strings.

There is a parallel hierarchy topped with nsACString, etc., that refers to single-byte strings.) nsAString guarantees only a string of characters. nsASingleFragmentString guarantees that the characters will be stored in a single buffer. nsAFlatString guarantees that the characters will be stored in a single null-terminated buffer. While there are underlying concrete classes, in general it is best to use the most abstract type possible in a given situation. For example, concantenation can be done virtually, through the use of pointers, resulting in an nsAString that can be used like any other string. This saves the allocating and copying that would otherwise have to be done. For more information, see "Guide to the Mozilla string classes".

XUL/XBL

Use of this final Mozilla technology is optional, depending on how you decide to create the user interface for your application. XUL is Mozilla's highly flexible XML UI Language. It provides a number of largely platform independent widgets from which to construct a UI. Netscape and Mozilla both use XUL for their interfaces, but not all embedders choose to use it. XBL or the eXtensible Binding Language allows you to attach behaviors to XUL's XML elements. More information on XUL can be found at XUL Programmer's Reference and on XBL at XBL:XBL_1.0_Reference. There is also a wealth of good information on XUL at XULPlanet.

Choosing Additional Functionalities

As of this writing (8/19/02), Gecko is a partially modularized rendering engine. Some functionalities beyond basic browsing are always embedded with Gecko, and, as a result of certain architectural decisions, always will be; some are at present always embedded with Gecko, but may, at some point in the future, be separable; and some are now available purely as options. The following table describes the present status of these additional functionalities:

Functions Status Now Status in Future
FTP support Optional
HTTPS support Optional
International character support Optional
XUL support Required Probably optional
Network support Required Maybe optional
JavaScript support Required Maybe optional
CSS support Required Always required
DOM support Required Probably always
XML support Required Probably always

At this time embedding Mozilla's editor along with the rendering engine Gecko is an uncertain proposion, although the situation continues to improve. For more information on the status of the embeddable editor, see http://www.mozilla.org/editor/Editor_Embedding_Guide.html.

What Gecko Provides

The following is a description of some of the interfaces most commonly used in embedding Gecko. It is by no means an exhaustive list of the available interfaces. The interfaces in this section are on classes provided by Mozilla. There is also a set of interfaces for which Gecko expects the embedder to provide the implementation. A sample of those are covered in the next section.

Initialization and Teardown

There are two C++ only functions which serve to initalize and terminate Gecko. The initialization function (NS_InitEmbedding) must be called before attempting to use Gecko. It ensures XPCOM is started, creates the component registry if necessary, and starts global services. The shutdown function (NS_TermEmbedding) terminates the Gecko embedding layer, ensuring that global services are unloaded, files are closed and XPCOM is shut down.

nsIWebBrowser

Use of this interface during initialization allows embedders to associate a new nsWebBrowser instance (an object representing the "client-area" of a typical browser window) with the embedder's chrome and to register any listeners. The interface may also be used at runtime to obtain the content DOM window and from that the rest of the DOM.

The XULPlanet nsWebBrowser reference also has a lot of useful information on this class.

nsIWebBrowserSetup

This interface is used to set basic properties (like whether image loading will be allowed) before the browser window is open.

nsIWebNavigation

The nsIWebNavigation interface is used to load URIs into the web browser instance and provide access to session history capabilities - such as back and forward. As of June 6, 2006, this interface is not yet frozen.

nsIWebBrowserPersist

The nsIWebBrowserPersist interface allows a URI to be saved to file. As of June 6, 2006, this interface is not yet frozen.

nsIBaseWindow

The nsIBaseWindow interface describes a generic window and basic operations (size, position, window title retrieval, etc.) that can be performed on it. As of June 6, 2006, this interface is not yet frozen.

nsISHistory

The nsISHistory interface provides access to session history information and allows that information to be purged.

nsIWebBrowserFind

The nsIWebBrowserFind interface controls the setup and execution of text searches in the browser window.

What You Provide

The following is a description of some of the more common embedder-provided interfaces used in embedding Gecko. It is by no means an exhaustive list of the available interfaces.

nsIWebBrowserChrome

The nsIWebBrowserChrome interface corresponds to the top-level, outermost window containing an embedded Gecko web browser. You associate it with the WebBrowser through the nsIWebBrowser interface. It provides control over window setup and whether or not the window is modal. It must be implemented.

nsIEmbeddingSiteWindow

The nsIEmbeddingSiteWindow interface provides Gecko with the means to call up to the host to resize the window, hide or show it and set/get its title. It must be implemented.

nsIWebProgressListener

The nsIWebProgressListener interface provides information on the progress of loading documents. It is added to the WebBrowser through the nsIWebBrowser interface. It must be implemented. As of this writing (8/19/02), it is not frozen.

nsISHistoryListener

The nsISHistoryListener interface is implemented by embedders who wish to receive notifications about activities in session history. A history listener is notified when pages are added, removed and loaded from session history. It is associated with Gecko through the nsIWebBrowser interface. Implementation is optional.

nsIContextMenuListener

The nsIContextMenuListener interface is implemented by embedders who wish to receive notifications for context menu events, i.e. generated by a user right-mouse clicking on a link. It should be implemented on the web browser chrome object associated with the window for which notifications are required. When a context menu event occurs, the browser will call this interface if present. Implementation is optional.

nsIPromptService

The nsIPromptServices interface allows the embedder to override Mozilla's standard prompts: alerts, dialog boxes, and check boxes and so forth. The class that implements these embedder specific prompts must be registered with the Component Manager using the same CID and contract ID that the Mozilla standard prompt service normally uses. Implementation is optional. As of this writing (8/19/02), this interface is not frozen.

Common Embedding Tasks

The following is a series of code snippets (taken from MFCEmbed, the Windows based embedding Gecko sample) which demonstrate very briefly implementation associated with common embedding tasks.To see all the files associated with this sample, go to http://lxr.mozilla.org/seamonkey/source/embedding/tests/mfcembed/. There are also Linux- and Mac OS-based examples.

Gecko setup

The Gecko embedding layer must be initialized before you can use Gecko. This ensures XPCOM is started, creates the component registry if necessary, and starts global services. There is an equivalent shutdown procedure.

Note that the embedding layer is started up by passing it two parameters. The first indicates where the executable is stored on the file system (nsnull indicates the working directory). The second indicates the file location object "provider" that specifies to Gecko where to find profiles, the component registry preferences, and so on.

nsresult rv;
rv = NS_InitEmbedding(nsnull, provider);
if(NS_FAILED(rv))
{
ASSERT(FALSE);
return FALSE;
} 

Creating a browser instance

The embedder-provided BrowserView object calls its method CreateBrowser(). Each browser object (a webbrowser) represents a single browser window. Notice the utility directive do_CreateInstance() and the use of macros.

//Create an instance of the Mozilla embeddable browser
 
HRESULT CBrowserView::CreateBrowser() 
{
// Create a web shell  
nsresult rv;
mWebBrowser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID, &rv);
if(NS_FAILED(rv))
return rv; 

Once the nsWebBrowser object is created the application uses do_QueryInterface() to load a pointer to the nsIWebNavigation interface into the mWebNav member variable. This will be used later for web page navigation.

rv = NS_OK;
mWebNav = do_QueryInterface(mWebBrowser, &rv);
if(NS_FAILED(rv))
return rv; 

Next the embedder-provided CBrowserImpl object is created. Gecko requires that some interfaces be implemented by the embedder so that Gecko can communicate with the embedding application. See the What You Provide Section. In the sample, CBrowserImpl is the object that implements those required interfaces. It will be passed into the SetContainerWindow() call below.

mpBrowserImpl = new CBrowserImpl();
if(mpBrowserImpl == nsnull)
return NS_ERROR_OUT_OF_MEMORY; 

The mWebBrowser interface pointer is then passed to the CBrowserImpl object via its Init() method. A second pointer to the platform specific BrowserFrameGlue interface is also passed in and saved. The BrowserFrameGlue pointer allows CBrowserImpl to call methods to update status bars, progress bars, and so forth.

mpBrowserImpl->Init(mpBrowserFrameGlue, mWebBrowser);
mpBrowserImpl->AddRef(); 

Next the embedder-supplied chrome object is associated with the webbrowser. Note the use of an nsCOMPtr.

mWebBrowser->SetContainerWindow
	(NS_STATIC_CAST(nsIWebBrowserChrome*, mpBrowserImpl)); 
nsCOMPtr<nsIWebBrowserSetup>setup(do_QueryInterface(mWebBrowser));  
if (setup) 
	setup->SetProperty(nsIWebBrowserSetup::SETUP_IS_CHROME_WRAPPER,PR_TRUE); 

Then, the real webbrowser window is created.

rv = NS_OK;
mBaseWindow = do_QueryInterface(mWebBrowser, &rv);
if(NS_FAILED(rv))
return rv; 

Binding a window

Basic location information is passed in.

RECT rcLocation;
GetClientRect(&rcLocation);
if(IsRectEmpty(&rcLocation)) 
{
	rcLocation.bottom++;
	rcLocation.top++;
}
rv = mBaseWindow->InitWindow(nsNativeWidget(m_hWnd), 
		nsnull,0, 0, rcLocation.right - rcLocation.left, 
		rcLocation.bottom - rcLocation.top);
rv = mBaseWindow->Create(); 

Note the m_hWnd passed into the call above to InitWindow(). (CBrowserView inherits the m_hWnd from CWnd). This m_hWnd will be used as the parent window by the embeddable browser.

Adding a listener

The BrowserImpl object is added as an nsIWebProgressListener. It will now receive progress messages. These callbacks will be used to update the status/progress bars.

nsWeakPtr weakling
	(dont_AddRef(NS_GetWeakReference(NS_STATIC_CAST(nsIWebProgressListener*, 
			mpBrowserImpl))));
void mWebBrowser->AddWebBrowserListener(weakling, NS_GET_IID(nsIWebProgressListener)); 

Finally the webbrowser window is shown.

mBaseWindow->SetVisibility(PR_TRUE);

Using session history to navigate

The pointer to nsIWebNavigation saved above is used to move back through session history.

void CBrowserView::OnNavBack() 
{
if(mWebNav)
	mWebNav->GoBack();
} 

Appendix: Data Flow Inside Gecko

While it isn't strictly necessary for embedders to understand how Gecko does what it does, a brief overview of the main structures involved as Gecko puts bits on a display may be helpful.

Image:EmbeddingBasicsa.gif

HTML data comes into Gecko either from the network or a local source. The first thing that happens is that it is parsed, using Gecko's own HTML parser. Then the Content Model arranges this parsed data into a large tree. The tree is also known as the "Document" and its structure is based on the W3C Document Object Model. Any use of DOM APIs manipulates the data in the Content Model.

Next the data is put into frames using CSS and the Frame Constructor. A frame in this sense (which is not the same thing as an HTML frame) is basically an abstract box within which a DOM element will be displayed. This process produces a Frame Tree, which, like the Content Model, is a tree of data, but this time focused not on the logical relationship among the elements but on the underlying calculations needed to display the data. In the beginning a frame has no size. Using CSS rules specifying how the elements of the DOM should look when they are displayed, including information like font type or image size, the eventual size of each frame is calculated. Because the same data may need to be displayed in different ways -- to a monitor and to a printer, for example -- a particular Content Model may have more than one Frame Tree associated with it. In such a case, each individual Frame Tree would belong to a different "presentation" mode.

Calculations continue as new information flows into the system using a process called reflow. As information in the Frame Tree changes, the section of the Frame Tree involved is marked "dirty" by the Frame Constructor. Reflow repeatedly steps through the tree, processing every "dirty" item it encounters until all the items it encounters are "clean". Every item in the Frame Tree has a pointer back to its corresponding item in the Content Model. A change in the Content Model, say through using the DOM APIs to change an element from hidden to visible, produces an equivalent change in the Frame Tree. It's important to note that all of these operations are purely data manipulations. Painting to the display itself is not yet involved at this point.

The next stage is the View Manager. With a few small exceptions that have to do with prompting the Frame Constructor to load graphics, the View Manager is the first place in the process that accesses the native OS. Delaying OS access until this point both helps Gecko to run more quickly and makes cross-platform issues easier to deal with. The View Manger is the place where Gecko figures out where on the display the data will need to be drawn. It then tells the system that that area is "invalid" and needs to be repainted. The actual painting is managed by the gfx submodule, while other low-level system operations are run through the widget submodule, which handles things like platform specific event (mouse clicks and so forth) processing loops and accessing system defaults (colors, fonts, etc.) Both gfx and widget are system specific.

If you want to take a look at the code underlying these structures, the code for the Content Model can be found in <tt>/mozilla/content</tt>, for the Frame Constructor, CSS, and Reflow in <tt>/mozilla/layout</tt>, for the View Manager in <tt>/mozilla/view</tt>, and for the DOM APIs in <tt>/mozilla/dom</tt>.

Original Document Information

리비전 소스

<p>정보의 원천, 놀이, 개인간 교류 수단으로써 웹의 중요성이 끝없이 증가하면서, HTML 포맷으로 저장된 데이터에 접근하고 보는 능력은 다양한 소프트웨어 애플리케이션을 폭넓게 대체하기 위해 점점 더 중요해지고 있다. 단순한 HTML 페이지 뷰어건 완벽한 기능의 웹 브라우저이건, HTML 기반 문서를 해석하고 그려내는 것은 수많은 상황에서 매우 중요한 기능이 됐다. 애플리케이션 개발자는 개발 시간을 최소화하면서도 애자일하고 안정성이 높은 제품을 만들기 위해, HTML을 출력하는 결정적인 기능을 어떻게 구현할지에 고민하게 되었다. Gecko, 즉 넷스케이프와 모질라 브라우저의 핵심에 있는 렌더링 엔진을 임베딩하는 것은 이런 문제에 대한 훌륭한 해결책이다.
</p>
<h2 name=".EC.99.9C_Gecko.EC.9D.B8.EA.B0.80.3F"> 왜 Gecko인가? </h2>
<p>Gecko를 임베딩하는 것은 현명한 선택이다. Gecko는 빠르고, 안정적이며 표준을 매우 잘 지키고 있다. Mozilla와 Netscape의 지원 아래서 매우 널리 배포되었으며 많은 사람들이 꾸준히 지켜보고 있다.
</p><p>Gecko는 오픈 소스이다. 다른 임베딩 브라우저 솔루션과는 달리, Gecko의 모든 소스 코드는 자유롭게 사용할 수 있으며 마음껏 수정할 수 있다. 필요한 만큼 뜯어 고칠 수 있다. 또한 현재의 라이센스에 따르면, Gecko를 독점적인 상용 제품에 컴포넌트로 사용할 수도 있다.
</p><p>Gecko는 Mozilla 프로젝트와 연관되어 있으므로, Mozilla 프로젝트에서 임베딩 작업에 도움이 되는 많은 리소스를 쉽게 얻을 수 있다. Mozilla <a class="external" href="http://www.mozilla.org">웹 사이트</a>에는 <a class="external" href="http://mozilla.org/projects/embedding/">임베딩 프로젝트 영역</a>이 있다. 또한 mozilla.dev.emobedding 뉴스그룹이 있으며, 많은 관련 뉴스그룹과 함께 임베딩 개발자들 간에 정보를 교환하는 것을 주 목적으로 하고 있다. 전체 <a class="external" href="http://lxr.mozilla.org/seamonkey">코드기반 상호 참조</a>가 가능하며, <a class="external" href="http://bugzilla.mozilla.org/">Bugzilla 버그 데이터베이스</a>를 통해 버그 제출, 버그 진행 상황 참조, 버그 수정 참여 등을 간단히 할 수 있다.
</p><p>Gecko는 바닥부터 크로스플랫폼으로 설계되었다. mozill.org에서는 직접 Wintel, MacOS 9.0과 OS X, 리눅스를 지원하고 있으며, 수많은 다른 플랫폼도 써드파티에서 지원하고 있다.
</p><p>마지막으로, Gecko를 라이센스하는 것은 로열티가 필요 없으며, 심지어 독점 저작권을 갖는 상용 제품에도 로열티없이 적용할 수 있다. "매우" 일반적으로는, 모질라가 제공하는 원본 소스코드에 대해서는 어떤 수정도 커뮤니티에 반환되어야 한다.(Gecko를 임베드하는 코드까지 반환해야하는 것은 아니다.) 즉 애플리케이션의 사용자라면 사용중인 Gecko 엔진에 해당하는 소스 코드를 언제든지 얻을 수 있어야 하며(대부분 mozilla.org 웹사이트 링크를 통해서), 애플리케이션은 명확한 방법으로 Gecko를 사용하고 있음을 공지해야 한다. (예를 들면, 제품 박스 위의 로고나 About: 페이지 등) 가능한 라이센스 방법에 대한 정확한 설명은 라이센스에 대해 유일한 법적 근거인 <a class="external" href="http://www.mozill.org/MPL/">Mozilla &amp; Nescape Public Licneses</a>에서 볼 수 있다.
</p>
<h2 name=".EC.9E.84.EB.B2.A0.EB.94.A9.EC.97.90_.ED.95.84.EC.9A.94.ED.95.9C_.EA.B2.83"> 임베딩에 필요한 것 </h2>
<p>Gecko를 임베드하기로 결정했다면, 세가지 과정을 밟아야 한다. 첫째로 코드를 얻는다. 다음으로 Gecko 코드베이스를 다루는데 사용되는 몇가지 지정 기술을 이해해야 한다. 마지막으로 추가하려는 부가적인 기능을 결정해야 한다. 이 절은 이러한 과정을 안내할 것이다.
</p>
<h3 name=".EC.BD.94.EB.93.9C_.EC.96.BB.EA.B8.B0"> 코드 얻기 </h3>
<p>이 글을 쓰는 시점에서 Gecko를 임베드하기 위한 파일을 얻는 가장 좋은 방법은 전체 Mozill 소스 트리를 다운로드해서 빌드하는 것이다. 사실 상당히 간단한 과정이다. 전체 설명과 적절한 링크는 <a href="ko/Download_Mozilla_Source_Code">Download Mozilla Source Code</a>에서 찾을 수 있다. 두번째 방법(컴포넌트에 의한 컴포넌트)은 현재 개발 중이며, 아직 베타 상태에 있다. 이 프로젝트에 대한 정보는 <a class="external" href="http://www.mozilla.org/projects/embedding/bootstrap.html">REQUIRES 기반 빌드 메카니즘</a>에서 찾을 수 있다. 그 외에, Gecko 런타임 환경(<a href="ko/GRE">GRE</a>, Gecko Runtime Environment)가 개발중에 있다. <a href="ko/GRE">GRE</a>는 단일 코어 라이브러리를 사용하는 모질라 컴포넌트 위에서 여러가지 애플리케이션을 지원하기 위한 것이다. (컴포넌트에 의한 컴포넌트를 사용하려고 생각한다면, 우선 버전과 바이너리 호환성에 대한 이슈를 잘 살펴봐야 한다. <a class="external" href="http://mozilla.org/projects/xpcom/glue/Component_Reuse.html">XPCOM 컴포넌트 재사용</a>을 살펴보면 도움이 될 것이다.)
</p><p>우선 몇가지 도구(기본으로 컴파일러, 펄 배포판, 몇몇 범용 유틸리티)를 가져와야 한다. 그런 다음 컴퓨터 환경을 설정하고, 소스를 다운로드 한다. 전체 트리를 다운로드하려 한다면, 두가지 방법이 있다. FTP에서 전체 소스 트리를 tar로 압축한 파일을 가져올 수 있다. (이것이 가장 간단한 방법이며, 확실히 컴파일이 가능한 한 방법이다. 다만 최근에 수정된 코드는 포함하지 않았을 수도 있다.) 다른 방법은 CVS에서 가장 최신의 코드를 가져오거나 변경분에 대한 업데이트를 수행하는 것이다. 소스를 가져와서 환경 설정을 적절히 끝마친 다음에는 제공된 makfile 중 적절한 것을 실행하면 된다. 각 플랫폼마다 컴파일 과정에 대한 상세한 설명이 있다.
</p><p>빌드가 끝나면 mozilla/embedding/config 디렉토리로 간다. 거기서 예제 manifest 파일을 찾을 수 있을 것이다. (파일들은 모두 이름이 "basebrowser"로 시작한다. manifest 파일은 각 플래폼마다 임베딩을 위한 개별 설정이다. 기본으로 들어있는 것은 단지 예제일 뿐이며, 당신의 목적에는 딱 들어맞지 않을 수도 있다. 하지만 예제로 시작해서 조금씩 수정해 나가면 좋을 것이다. 각 플랫폼마다 모델로 삼을 만한 예제 임베딩 프로젝트들도 있다. <a href="ko/Roll_your_own_browser_-_An_embedding_HowTo">Roll_your_own_browser_-_An_embedding_HowTo</a>를 참고하라.
</p>
<h3 name=".EC.BD.94.EB.93.9C_.EC.9E.91.EC.84.B1_.ED.99.98.EA.B2.BD_.EC.9D.B4.ED.95.B4.ED.95.98.EA.B8.B0"> 코드 작성 환경 이해하기 </h3>
<p>모질라는 처음부터 여러 플랫폼과 프로그래밍 언어에 대한 설계와 개발을 지원하는 것으로 시작됐다. 결국 여러가지 자체 프로그래밍 기술이 개발되었으며, 모두 다 객체 은폐라는 이상적인 개념을 바탕으로 두고 있다. Gecko 임베딩에는 XPCOM, XPIDL, XPConnect, 특별 문자열 클래스, XUL 등의 기술에 대한 실제적인 지식을 필요로 한다. 다음은 각 기술에 대한 간략한 소개이다. 더 자세한 정보는 mozilla.org 사이트에서 찾을 수 있다.
</p>
<h4 name="XPCOM"> XPCOM </h4>
<p>모질라 기술 중 가장 중요한 것은 <a href="ko/XPCOM">XPCOM</a>, 즉 크로스플랫폼 컴포넌트 오브젝트 모델(Cross-Platform Component Object Model)이다. XPCOM은 객체나 다른 데이터의 생성, 소유, 제거를 모질라 전체에 걸쳐 관리하는 프레임워크를 제공한다. MSCOM을 사용한 적이 있다면, 기본적으로 유사한 개념임을 느낄 것이다. 하지만 매우 큰 차이가 있다. XPCOM은 크로스플랫폼이며 단일 스레드에서 대부분 동작하도록 설계되어 있다. 또한 XPCOM와 MSCOM은 현재로써는 아직 호환되지 않는다.
</p>
<h5 name=".EC.9D.B8.ED.84.B0.ED.8E.98.EC.9D.B4.EC.8A.A4"> 인터페이스 </h5>
<p>XPCOM의 핵심은 인터페이스라는 개념이다. 인터페이스는 특정 기증과 연관된 메소드, 속성, 관련 상수의 집합을 기술한 것일 뿐이다. 실제 기능을 구현하는 클래스와는 전혀 별개이다. 인터페이스는 계약의 역할을 한다. 특정 인터페이스를 지원하는 객체는 인터페이스에 기술된 서비스를 수행한다는 약속의 의미이다. 인터페이스를 최대한 중립적으로 유지할 수 있도록 IDL(Interface Definition Language)라는 별도의 언어를 사용해서 기술한다. 인터페이스 파일은 일반저긍로 .idl이라는 확장자를 갖는다. 인터페이스의 기능을 지정할 뿐만 아니라 인터페이스의 IID도 지정한다. IID는 인터페이스를 지정할 때 사용하는 세계에서 유일한 번호이다.
</p><p>Gecko 내에서 이루어지는 통신의 대부분은 추상 구조체를 거친다. (규약에 따라 추상 구조체의 이름은 <code>nsISomething</code>의 형식을 따른다.)
</p>
<pre>//이거야
void ProcessSample(nsISample* aSample) {
	aSample-&gt;Poke("Hello"); 
//이건 아니야
void ProcessSample(nsSampleImpl* aSample) {
	aSample-&gt;Poke("hello"); 
</pre>
<h5 name=".40status_FROZEN"> @status FROZEN </h5>
<p>XPCOM의 추상화 수준으로 인해 시스템이 매우 큰 유연성을 갖추게 됐다.
구현은 필요에 따라 얼마든지 변경할 수 있다. 하지만 최소한 인터페이스 자체는
고정된 채로 유지해야 한다. 모질라의 초기 설계와 개발 기간 동안에는 인터페이스는
다소 유동적인 것이었지만, 프로젝트가 성숙되면서 점점 더 많은 인터페이스가
FROZEN으로 표시됐다. FROZEN으로 표시된 인터페이스는 향후 변경되지 않음을
보장한다는 의미이다.
</p><p>임베딩 관련된 대부분의 주요 인터페이스가 FROZEN으로 표시되긴 했지만,
인터페이스를 사용하기 전에 확인해보는 것이 좋다. 인터페이스의 상태는
.idl 파일의 주석에 나열된다. 얼려진 인터페이스(frozen interface)는
<code>@status FROZEN</code>라는 표시가 달린다.
모질라 상호 참조 도구를 사용해서 <a class="external" href="http://lxr.mozilla.org/seamonkey/search?string=%40status+FROZEN">얼려진 인터페이스를 검색</a>할 수도 있다.
얼려지지 않은 상태의 인터페이스는 언제든지 변경될 가능성이 있다.
얼리는 과정에 대한 자세한 정보는 <a class="external" href="http://mozilla.org/embedding/">임베딩 프로젝트 페이지</a>를 참고하라.
</p><p>인터페이스가 얼려지고 나면 해당 인터페이스는
<a class="external" href="http://mozilla.org/projects/embedding/embedapiref/embedapi.html">Gecko 임베딩 API 참조</a>에 추가된다.
</p>
<h5 name="nsISupports"> nsISupports </h5>
<p>한 객체가 하나 이상의 인터페이스를 지원할 수 있다. 실제로는 모든 객체는 최소한 두 개의 인터페이스를 지원해야 한다. 하나는 실제 객체의 역할이 되는 무언가이고, 다른 하나는 범용적인 목적으로 사용되는 <code>nsISupports</code>이다. 다르게 말하자면 <code>nsISupports</code>는 모든 XPCOM 인터페이스의 조상이다. 모든 인터페이스는 <code>nsISupports</code>로부터 상속되며, 대부분은 직접 상속받는다. <code>nsISupports</code>는 두가지 주요 기능을 갖는다. 런타임 타입 발견과 객체 생명주기 관리이다. MSCOM의 IUnknown과 동일한 역할을 한다.
</p><p>모든 객체가 다중 인터페이스를 지원하기 때문에, 한 인터페이스에 대한 포인터를 가진 상태에서 해당 객체가 당장 필요한 기능을 제공하는 다른 인터페이스를 지원하는지 알아낼 수 있다. 첫번째 <code>nsISupports</code> 메소드인 <code>QueryInterface()</code>는 다음과 같은 질문을 수행한다. "나는 이 객체가 A 타입임(인터페이스 A를 지원함)을 알고 있다. 그런데 타입 B이기도 한가?(인터페이스 B도 지원하는가?)"
</p><p>만일 그렇다면 <code>QueryInterface()</code>는 새로 요청된 인터페이스로 연결된 포인터를 반환한다.
</p>
<pre>void ProcessSample(nsISample* aSample) {
	nsIExample *example;
	nsresult rv;
	rv = aSample-&gt;QueryInterface(NS_GET_IID(nsIExample),(void **)&amp;example); 
	if (NS_SUCCEEDED(rv)) {
		example-&gt;DoSomeOperation();
		NS_RELEASE(example); // Release호출을 위해 매크로를 사용 
	} 
} 
</pre>
<p>XPCOM은 간접적인 방법(컴포넌트 관리자)을 사용해서 객체의 인스턴스를 생성하기 때문에, 같은 객체에 대한 다중 포인터(각각 다양한 인터페이스로 연결됨)가 존재할 수 있으며, 포인터가 가리키는 모든 객체를 호출자가 정확하게 추적하기 어려워질 수 있다. 객체가 필요하지 않은 상황에서도 남아있게 되서 메미로 누수가 발생하거나, 필요한 객체가 삭제되는 바람에 댕글링 포인터가 나타날 수도 있다. <code>nsISupports&lt;code&gt;의 다른 두 메소드 &lt;code&gt;AddRef()</code>와 <code>Release()</code>는 이런 이슈를 다루기 위해 설계된 것이다. 포인터가 변수에 입력될 때마다<code>AddRef()</code>가 호출되어 내부 카운터가 증가한다. 포인터가 풀려날 때마다 <code>Release()</code>가 호출되어 내부 카운터가 감소한다. 카운터가 0이 되면, 객체에 대한 포인터가 없으며 객체를 안전하게 삭제할 수 있다. 객체의 수명에 대한 제어는 객체 내부에 있는 것이다. 이와 같은 과정을 자동화할 수 있도록 돕는 유틸리티인 XPCOM "스마트" 포인터<a href="#nsCOMPtr">nsCOMPtr</a>에 대한 정보를 참고하라.
</p>
<h5 name="Object_creation"> Object creation </h5>
<p>The instantiation of objects is also an indirect process in XPCOM. Just as interfaces have a globally unique ID number (the IID), XPCOM classes are assigned their own GUIDs, the CID. In addition, they are also often given a text-based ID, called a contract ID. One or the other of these IDs is passed to a method on a persistent XPCOM component, the Component Manager, which actually creates the object. When a new library of classes (called a module in XPCOM) is first introduced into the system, it must register itself with the Component Manager, which maintains a registry that maps classes (with their IDs) to the libraries in which they reside.
</p><p>A limited number of persistent services, supplied by singleton objects, are created and controlled by a companion to the Component Manager, the Service Manager. The Component Manager itself is an example of such a persistent service.
</p>
<h5 name="Summing_up"> Summing up </h5>
<p>Functionality in XPCOM is described by abstract interfaces, and most communication among parts of the system takes place in terms of those interfaces. The underlying objects that implement the interfaces, on the other hand, are created indirectly by the Component Manager based on a cross-indexed registry that it maintains.
</p><p>One functionality shared by all interfaces is the ability to query the underlying object at runtime to see if also implements other interfaces. In theory an interface is fixed and unchangeable, but at this stage in the Mozilla codebase, only interfaces that have been declared <code>FROZEN</code> are guaranteed not to change significantly. Object lifetime management takes place inside the object itself through an internal counter that keeps track of the number of pointers to the object that have been added or released. The client's only responsibility is to increment and decrement the counter. When the internal counter reaches zero, the object deletes itself.
</p>
<h5 name="nsCOMPtr"> nsCOMPtr </h5>
<p>Sometimes, however, even remembering to call <code>AddRef()</code> and <code>Release()</code> at the right times can be difficult. To make this process easier and more reliable, XPCOM has a built-in "smart" pointer, <code>nsCOMPtr</code>. This pointer takes care of calling <code>AddRef()</code> and <code>Release()</code> for you. Using <code>nsCOMPtr</code> whenever possible will make your code cleaner and more efficient. For more information on the smart pointer, see "<a class="external" href="http://www.mozilla.org/projects/xpcom/nsCOMPtr.html">The Complete nsCOMPtr User's Manual</a>".
</p><p>Mozilla actually provides a large number of built-in macros (by convention, written in all caps in the code) and utilities like <code>nsCOMPtr</code> that can make the entire process of coding with XPCOM easier. Many of these can be found in the following files: <tt>nsCom.h</tt>, <tt>nsDebug.h</tt>, <tt>nsError.h</tt>, <tt>nsIServiceManager.h</tt>, and <tt>nsISupportsUtils.h</tt>. Mozilla also supplies other development tools for tracking memory usage and the like. More information on these can be found at http://www.mozilla.org/performance/
</p>
<h5 name="For_more_information"> For more information </h5>
<p>More information on XPCOM in general can be found at <a href="ko/XPCOM">XPCOM</a>. For an overview of creating XPCOM components, see Chapter 8 of O'Reilly's <i><a class="external" href="http://books.mozdev.org/chapters/ch08.html">Creating Applications with Mozilla</a></i>. There is also a new book completely devoted to this topic, <i><a href="ko/Creating_XPCOM_Components">Creating XPCOM Components</a></i>. A fuller explanation of some of the underlying logic to COM systems can be found in the early chapters of <i>Essential COM</i> by Don Box. While it focuses on MSCOM in particular, the book does provide an excellent background on some of the core rationales for using such an object model.
</p>
<h4 name="XPIDL"> XPIDL </h4>
<p>Interfaces are abstract classes written in XPIDL, the Cross Platform Interface Definition Language. Yet to be useful the functionality promised in those interfaces must be implemented in some regular programming language. Facilitating this is the job of the XPIDL compiler. Once an interface is defined in an .idl file, it can be processed by the XPIDL compiler.
</p><p>The compiler can be set to output a number of things, but generally the output is two-fold: a C++ .h file that includes a commented out template for a full C++ implementation of the interface and an XPT file that contains type library information which works with XPConnect to make the interface available to JavaScript. More information on the syntax of <a href="ko/XPIDL">XPIDL</a> (a simple C-like language) and the use of the <a href="ko/XPIDL/xpidl">compiler</a> is available.
</p>
<h4 name="XPConnect_and_XPT_files"> <a href="ko/XPConnect">XPConnect</a> and XPT files </h4>
<p><a href="ko/XPConnect">XPConnect</a> is an <a href="ko/XPCOM">XPCOM</a> module that allows code written in <a href="ko/JavaScript">JavaScript</a> to access and manipulate XPCOM components written in C++ and vice versa. By means of XPConnect, components on either side of an XPCOM interface do not, in general, need to know or care about which of these languages the object on the other side is implemented in.
</p><p>When an interface is run through the XPIDL compiler, it produces an XPT or type library file. Because XPconnect uses the information in this file to implement transparent communication between C++ objects and JavaScript objects across XPCOM interfaces, it is important to make sure they are generated and included with your code even if you are developing exclusively in C++. Not only is a substantial part of the browser, in fact, implemented in JS, it is possible that in the future someone may wish to use JS-based code to interact with whatever components you create .
</p><p>As is from Mozilla, XPConnect currently facilitates interoperability between C++ and JS. Modules to extend it to allow access from other languages (including Python) are under independent development.
</p>
<h4 name="String_classes"> String classes </h4>
<p>Web browsing typically involves a large amount of string manipulation. Mozilla has developed a hierarchy of C++ classes to facilitate such manipulation and to render it efficient and quick. To make communication among objects simpler and more error free, Mozilla uses interfaces, which are, in essence, abstract classes. The string hierarchy is also headed up by a set of abstract classes, <code>nsAString</code>, <code>nsASingleFragmentString</code>, and <code>nsAFlatString</code>, and for the same reasons. (These refer to double-byte strings.
</p><p>There is a parallel hierarchy topped with <code>nsACString</code>, etc., that refers to single-byte strings.) <code>nsAString</code> guarantees only a string of characters. <code>nsASingleFragmentString</code> guarantees that the characters will be stored in a single buffer. <code>nsAFlatString</code> guarantees that the characters will be stored in a single null-terminated buffer. While there are underlying concrete classes, in general it is best to use the most abstract type possible in a given situation. For example, concantenation can be done virtually, through the use of pointers, resulting in an nsAString that can be used like any other string. This saves the allocating and copying that would otherwise have to be done. For more information, see "<a href="ko/XPCOM/Strings">Guide to the Mozilla string classes</a>".
</p>
<h4 name="XUL.2FXBL"> XUL/XBL </h4>
<p>Use of this final Mozilla technology is optional, depending on how you decide to create the user interface for your application. <a href="ko/XUL">XUL</a> is Mozilla's highly flexible XML UI Language. It provides a number of largely platform independent widgets from which to construct a UI. Netscape and Mozilla both use XUL for their interfaces, but not all embedders choose to use it. XBL or the eXtensible Binding Language allows you to attach behaviors to XUL's XML elements. More information on XUL can be found at <a class="external" href="http://www.mozilla.org/xpfe/xulref/">XUL Programmer's Reference</a> and on <a href="ko/XBL">XBL</a> at <a href="ko/XBL/XBL_1.0_Reference">XBL:XBL_1.0_Reference</a>. There is also a wealth of good information on XUL at <a class="external" href="http://www.xulplanet.com/">XULPlanet</a>.
</p>
<h3 name="Choosing_Additional_Functionalities"> Choosing Additional Functionalities </h3>
<p>As of this writing (8/19/02), Gecko is a partially modularized rendering engine. Some functionalities beyond basic browsing are always embedded with Gecko, and, as a result of certain architectural decisions, always will be; some are at present always embedded with Gecko, but may, at some point in the future, be separable; and some are now available purely as options. The following table describes the present status of these additional functionalities:
</p>
<table>
<tbody><tr>
<th> Functions
</th><th> Status Now
</th><th> Status in Future
</th></tr>
<tr>
<td> FTP support
</td><td> Optional
</td><td>
</td></tr>
<tr>
<td> HTTPS support
</td><td> Optional
</td><td>
</td></tr>
<tr>
<td> International character support
</td><td> Optional
</td><td>
</td></tr>
<tr>
<td> XUL support
</td><td> Required
</td><td> Probably optional
</td></tr>
<tr>
<td> Network support
</td><td> Required
</td><td> Maybe optional
</td></tr>
<tr>
<td> JavaScript support
</td><td> Required
</td><td> Maybe optional
</td></tr>
<tr>
<td> CSS support
</td><td> Required
</td><td> Always required
</td></tr>
<tr>
<td> DOM support
</td><td> Required
</td><td> Probably always
</td></tr>
<tr>
<td> XML support
</td><td> Required
</td><td> Probably always
</td></tr></tbody></table>
<p>At this time embedding Mozilla's editor along with the rendering engine Gecko is an uncertain proposion, although the situation continues to improve. For more information on the status of the embeddable editor, see http://www.mozilla.org/editor/Editor_Embedding_Guide.html.
</p>
<h2 name="What_Gecko_Provides"> What Gecko Provides </h2>
<p>The following is a description of some of the interfaces most commonly used in embedding Gecko. It is by no means an exhaustive list of the available interfaces. The interfaces in this section are on classes provided by Mozilla. There is also a set of interfaces for which Gecko expects the embedder to provide the implementation. A sample of those are covered in the next section.
</p>
<h3 name="Initialization_and_Teardown"> Initialization and Teardown </h3>
<p>There are two C++ only functions which serve to initalize and terminate Gecko. The initialization function (<a class="external" href="http://mozilla.org/projects/embedding/embedapiref/embedapi2.html#1099700">NS_InitEmbedding</a>) must be called before attempting to use Gecko. It ensures XPCOM is started, creates the component registry if necessary, and starts global services. The shutdown function (<a class="external" href="http://mozilla.org/projects/embedding/embedapiref/embedapi2.html#1101115">NS_TermEmbedding</a>) terminates the Gecko embedding layer, ensuring that global services are unloaded, files are closed and XPCOM is shut down.
</p>
<h3 name="nsIWebBrowser"> <a class="external" href="http://mozilla.org/projects/embedding/embedapiref/embedapi4.html">nsIWebBrowser</a> </h3>
<p>Use of this interface during initialization allows embedders to associate a new <code>nsWebBrowser</code> instance (an object representing the "client-area" of a typical browser window) with the embedder's chrome and to register any listeners. The interface may also be used at runtime to obtain the content DOM window and from that the rest of the DOM.
</p><p>The <a class="external" href="http://xulplanet.com/references/xpcomref/ifaces/nsIWebBrowser.html">XULPlanet <code>nsWebBrowser</code> reference</a> also has a lot of useful information on this class.
</p>
<h3 name="nsIWebBrowserSetup"> <a class="external" href="http://www.mozilla.org/projects/embedding/embedapiref/embedapi10.html">nsIWebBrowserSetup</a> </h3>
<p>This interface is used to set basic properties (like whether image loading will be allowed) before the browser window is open.
</p>
<h3 name="nsIWebNavigation"> <a class="external" href="http://www.xulplanet.com/references/xpcomref/ifaces/nsIWebNavigation.html">nsIWebNavigation</a> </h3>
<p>The <code>nsIWebNavigation</code> interface is used to load URIs into the web browser instance and provide access to session history capabilities - such as back and forward. As of June 6, 2006, this interface is not yet frozen.
</p>
<h3 name="nsIWebBrowserPersist"> <a class="external" href="http://www.xulplanet.com/references/xpcomref/ifaces/nsIWebBrowserPersist.html">nsIWebBrowserPersist</a> </h3>
<p>The <code>nsIWebBrowserPersist</code> interface allows a URI to be saved to file. As of June 6, 2006, this interface is not yet frozen.
</p>
<h3 name="nsIBaseWindow"> <a class="external" href="http://www.xulplanet.com/references/xpcomref/ifaces/nsIBaseWindow.html">nsIBaseWindow</a> </h3>
<p>The <code>nsIBaseWindow</code> interface describes a generic window and basic operations (size, position, window title retrieval, etc.) that can be performed on it. As of June 6, 2006, this interface is not yet frozen.
</p>
<h3 name="nsISHistory"> <a class="external" href="http://mozilla.org/projects/embedding/embedapiref/embedapi58.html">nsISHistory</a> </h3>
<p>The <code>nsISHistory</code> interface provides access to session history information and allows that information to be purged.
</p>
<h3 name="nsIWebBrowserFind"> <a class="external" href="http://mozilla.org/projects/embedding/embedapiref/embedapi14.html">nsIWebBrowserFind</a> </h3>
<p>The <code>nsIWebBrowserFind</code> interface controls the setup and execution of text searches in the browser window.
</p>
<h2 name="What_You_Provide"> What You Provide </h2>
<p>The following is a description of some of the more common embedder-provided interfaces used in embedding Gecko. It is by no means an exhaustive list of the available interfaces.
</p>
<h3 name="nsIWebBrowserChrome"> <a class="external" href="http://mozilla.org/projects/embedding/embedapiref/embedapi6.html">nsIWebBrowserChrome</a> </h3>
<p>The <code>nsIWebBrowserChrome</code> interface corresponds to the top-level, outermost window containing an embedded Gecko web browser. You associate it with the WebBrowser through the <code>nsIWebBrowser</code> interface. It provides control over window setup and whether or not the window is modal. It must be implemented.
</p>
<h3 name="nsIEmbeddingSiteWindow"> <a class="external" href="http://www.mozilla.org/projects/embedding/embedapiref/embedapi12.html">nsIEmbeddingSiteWindow</a> </h3>
<p>The <code>nsIEmbeddingSiteWindow</code> interface provides Gecko with the means to call up to the host to resize the window, hide or show it and set/get its title. It must be implemented.
</p>
<h3 name="nsIWebProgressListener"> <a href="ko/NsIWebProgressListener">nsIWebProgressListener</a> </h3>
<p>The <code>nsIWebProgressListener</code> interface provides information on the progress of loading documents. It is added to the WebBrowser through the <code>nsIWebBrowser</code> interface. It must be implemented. As of this writing (8/19/02), it is not frozen.
</p>
<h3 name="nsISHistoryListener"> <a class="external" href="http://mozilla.org/projects/embedding/embedapiref/embedapi59.html">nsISHistoryListener</a> </h3>
<p>The <code>nsISHistoryListener</code> interface is implemented by embedders who wish to receive notifications about activities in session history. A history listener is notified when pages are added, removed and loaded from session history. It is associated with Gecko through the <code>nsIWebBrowser</code> interface. Implementation is optional.
</p>
<h3 name="nsIContextMenuListener"> <a class="external" href="http://mozilla.org/projects/embedding/embedapiref/embedapi5.html">nsIContextMenuListener</a> </h3>
<p>The <code>nsIContextMenuListener</code> interface is implemented by embedders who wish to receive notifications for context menu events, i.e. generated by a user right-mouse clicking on a link. It should be implemented on the web browser chrome object associated with the window for which notifications are required. When a context menu event occurs, the browser will call this interface if present. Implementation is optional.
</p>
<h3 name="nsIPromptService"> <a href="ko/NsIPromptService">nsIPromptService</a> </h3>
<p>The <code>nsIPromptServices</code> interface allows the embedder to override Mozilla's standard prompts: alerts, dialog boxes, and check boxes and so forth. The class that implements these embedder specific prompts must be registered with the Component Manager using the same CID and contract ID that the Mozilla standard prompt service normally uses. Implementation is optional. As of this writing (8/19/02), this interface is not frozen.
</p>
<h2 name="Common_Embedding_Tasks"> Common Embedding Tasks </h2>
<p>The following is a series of code snippets (taken from MFCEmbed, the Windows based embedding Gecko sample) which demonstrate very briefly implementation associated with common embedding tasks.To see all the files associated with this sample, go to http://lxr.mozilla.org/seamonkey/source/embedding/tests/mfcembed/. There are also Linux- and Mac OS-based examples.
</p>
<h3 name="Gecko_setup"> Gecko setup </h3>
<p>The Gecko embedding layer must be initialized before you can use Gecko. This ensures XPCOM is started, creates the component registry if necessary, and starts global services. There is an equivalent shutdown procedure.
</p><p>Note that the embedding layer is started up by passing it two parameters. The first indicates where the executable is stored on the file system (<code>nsnull</code> indicates the working directory). The second indicates the file location object "provider" that specifies to Gecko where to find profiles, the component registry preferences, and so on.
</p>
<pre>nsresult rv;
rv = NS_InitEmbedding(nsnull, provider);
if(NS_FAILED(rv))
{
ASSERT(FALSE);
return FALSE;
} 
</pre>
<h3 name="Creating_a_browser_instance"> Creating a browser instance </h3>
<p>The embedder-provided BrowserView object calls its method <code>CreateBrowser()</code>. Each browser object (a webbrowser) represents a single browser window. Notice the utility directive <code>do_CreateInstance()</code> and the use of macros.
</p>
<pre>//Create an instance of the Mozilla embeddable browser
 
HRESULT CBrowserView::CreateBrowser() 
{
// Create a web shell  
nsresult rv;
mWebBrowser = do_CreateInstance(NS_WEBBROWSER_CONTRACTID, &amp;rv);
if(NS_FAILED(rv))
return rv; 
</pre>
<p>Once the <code>nsWebBrowser</code> object is created the application uses <code>do_QueryInterface()</code> to load a pointer to the <a class="external" href="http://www.xulplanet.com/references/xpcomref/ifaces/nsIWebNavigation.html">nsIWebNavigation</a> interface into the <code>mWebNav</code> member variable. This will be used later for web page navigation.
</p>
<pre>rv = NS_OK;
mWebNav = do_QueryInterface(mWebBrowser, &amp;rv);
if(NS_FAILED(rv))
return rv; 
</pre>
<p>Next the embedder-provided <code>CBrowserImpl</code> object is created. Gecko requires that some interfaces be implemented by the embedder so that Gecko can communicate with the embedding application. See the <a href="#What_You_Provide">What You Provide Section</a>. In the sample, <code>CBrowserImpl</code> is the object that implements those required interfaces. It will be passed into the <code>SetContainerWindow()</code> call below.
</p>
<pre>mpBrowserImpl = new CBrowserImpl();
if(mpBrowserImpl == nsnull)
return NS_ERROR_OUT_OF_MEMORY; 
</pre>
<p>The <code>mWebBrowser</code> interface pointer is then passed to the <code>CBrowserImpl</code> object via its <code>Init()</code> method. A second pointer to the platform specific <code>BrowserFrameGlue</code> interface is also passed in and saved. The <code>BrowserFrameGlue</code> pointer allows <code>CBrowserImpl</code> to call methods to update status bars, progress bars, and so forth.
</p>
<pre>mpBrowserImpl-&gt;Init(mpBrowserFrameGlue, mWebBrowser);
mpBrowserImpl-&gt;AddRef(); 
</pre>
<p>Next the embedder-supplied chrome object is associated with the webbrowser. Note the use of an <code>nsCOMPtr</code>.
</p>
<pre>mWebBrowser-&gt;SetContainerWindow
	(NS_STATIC_CAST(nsIWebBrowserChrome*, mpBrowserImpl)); 
nsCOMPtr&lt;nsIWebBrowserSetup&gt;setup(do_QueryInterface(mWebBrowser));  
if (setup) 
	setup-&gt;SetProperty(nsIWebBrowserSetup::SETUP_IS_CHROME_WRAPPER,PR_TRUE); 
</pre>
<p>Then, the real webbrowser window is created.
</p>
<pre>rv = NS_OK;
mBaseWindow = do_QueryInterface(mWebBrowser, &amp;rv);
if(NS_FAILED(rv))
return rv; 
</pre>
<h3 name="Binding_a_window"> Binding a window </h3>
<p>Basic location information is passed in.
</p>
<pre>RECT rcLocation;
GetClientRect(&amp;rcLocation);
if(IsRectEmpty(&amp;rcLocation)) 
{
	rcLocation.bottom++;
	rcLocation.top++;
}
rv = mBaseWindow-&gt;InitWindow(nsNativeWidget(m_hWnd), 
		nsnull,0, 0, rcLocation.right - rcLocation.left, 
		rcLocation.bottom - rcLocation.top);
rv = mBaseWindow-&gt;Create(); 
</pre>
<p>Note the <code>m_hWnd</code> passed into the call above to <code>InitWindow()</code>. (<code>CBrowserView</code> inherits the <code>m_hWnd</code> from <code>CWnd</code>). This <code>m_hWnd</code> will be used as the parent window by the embeddable browser.
</p>
<h3 name="Adding_a_listener"> Adding a listener </h3>
<p>The <code>BrowserImpl</code> object is added as an <a href="ko/NsIWebProgressListener">nsIWebProgressListener</a>. It will now receive progress messages. These callbacks will be used to update the status/progress bars.
</p>
<pre>nsWeakPtr weakling
	(dont_AddRef(NS_GetWeakReference(NS_STATIC_CAST(nsIWebProgressListener*, 
			mpBrowserImpl))));
void mWebBrowser-&gt;AddWebBrowserListener(weakling, NS_GET_IID(nsIWebProgressListener)); 
</pre>
<p>Finally the webbrowser window is shown.
</p>
<pre>mBaseWindow-&gt;SetVisibility(PR_TRUE);
</pre>
<h3 name="Using_session_history_to_navigate"> Using session history to navigate </h3>
<p>The pointer to <a class="external" href="http://www.xulplanet.com/references/xpcomref/ifaces/nsIWebNavigation.html">nsIWebNavigation</a> saved above is used to move back through session history.
</p>
<pre>void CBrowserView::OnNavBack() 
{
if(mWebNav)
	mWebNav-&gt;GoBack();
} 
</pre>
<h2 name="Appendix:_Data_Flow_Inside_Gecko"> Appendix: Data Flow Inside Gecko </h2>
<p>While it isn't strictly necessary for embedders to understand how Gecko does what it does, a brief overview of the main structures involved as Gecko puts bits on a display may be helpful.
</p><p><img alt="Image:EmbeddingBasicsa.gif" src="File:ko/Media_Gallery/EmbeddingBasicsa.gif">
</p><p>HTML data comes into Gecko either from the network or a local source. The first thing that happens is that it is parsed, using Gecko's own HTML parser. Then the Content Model arranges this parsed data into a large tree. The tree is also known as the "Document" and its structure is based on the W3C Document Object Model. Any use of DOM APIs manipulates the data in the Content Model.
</p><p>Next the data is put into frames using CSS and the Frame Constructor. A frame in this sense (which is not the same thing as an HTML frame) is basically an abstract box within which a DOM element will be displayed. This process produces a Frame Tree, which, like the Content Model, is a tree of data, but this time focused not on the logical relationship among the elements but on the underlying calculations needed to display the data. In the beginning a frame has no size. Using CSS rules specifying how the elements of the DOM should look when they are displayed, including information like font type or image size, the eventual size of each frame is calculated. Because the same data may need to be displayed in different ways -- to a monitor and to a printer, for example -- a particular Content Model may have more than one Frame Tree associated with it. In such a case, each individual Frame Tree would belong to a different "presentation" mode.
</p><p>Calculations continue as new information flows into the system using a process called <b>reflow</b>. As information in the Frame Tree changes, the section of the Frame Tree involved is marked "dirty" by the Frame Constructor. Reflow repeatedly steps through the tree, processing every "dirty" item it encounters until all the items it encounters are "clean". Every item in the Frame Tree has a pointer back to its corresponding item in the Content Model. A change in the Content Model, say through using the DOM APIs to change an element from hidden to visible, produces an equivalent change in the Frame Tree. It's important to note that all of these operations are purely data manipulations. Painting to the display itself is not yet involved at this point.
</p><p>The next stage is the View Manager. With a few small exceptions that have to do with prompting the Frame Constructor to load graphics, the View Manager is the first place in the process that accesses the native OS. Delaying OS access until this point both helps Gecko to run more quickly and makes cross-platform issues easier to deal with. The View Manger is the place where Gecko figures out where on the display the data will need to be drawn. It then tells the system that that area is "invalid" and needs to be repainted. The actual painting is managed by the gfx submodule, while other low-level system operations are run through the widget submodule, which handles things like platform specific event (mouse clicks and so forth) processing loops and accessing system defaults (colors, fonts, etc.) Both gfx and widget are system specific.
</p><p>If you want to take a look at the code underlying these structures, the code for the Content Model can be found in <tt>/mozilla/content</tt>, for the Frame Constructor, CSS, and Reflow in <tt>/mozilla/layout</tt>, for the View Manager in <tt>/mozilla/view</tt>, and for the DOM APIs in <tt>/mozilla/dom</tt>. 
</p>
<div class="originaldocinfo">
<h2 name="Original_Document_Information"> Original Document Information </h2>
<ul><li> Author(s): <a class="external" href="mailto:jeev@netscape.com">Ellen Evans</a>
</li><li> Last Updated Date: August 19, 2002
</li><li> Copyright Information: Copyright (C) <a class="external" href="mailto:jeev@netscape.com">Ellen Evans</a>
</li></ul>
</div>
Revert to this revision