파이어폭스 브라우저 소스코드 변경방법(번역)

얼마 전 부터 파이어폭스 브라우저에 대해 공부를 시작했다.
이를 위해 모질라 사이트의 여러 문서를 공부 중이며 그 일환으로 Mozilla-wiki 중 Modifying the Firefox Browser Source Code 를 번역한 것이다.
내용 중에 잘못된 것이 있거나 다른 의견이 있다면 댓글로 알려 주기를 희망한다.

———문서 제목 Education/Learning/ModifyBrowser ————-

Contents

  • 1. 들어가기
    • 1.1 소스코드 얻기
  • 2 . ‘무엇을’: 파이어폭스에서 새로운 탭을 여는 방법의 변경
  • 3 .’어디에서’: 올바른 ‘코드변경 위치’를 찾기
    • 3.1 검색 1 – UI 문자열 찾기
    • 3.2 검색 2 – 엔터티(ENTITY) 찾기
    • 3.3 검색 3 – 함수 찾기
  • .4 ‘어떻게’: 코드의 필수적인 변경
    • 4.1 첫번째 시도
    • 4.2 두번째 시도
    • 4.3 세번째 시도
    • 4.4 성공, 그리고 약간의 버그
  • 5 .맷는 말

들어가기

이번 연습에서는 파이어폭스의 행동에 약간의 변화를 생기게 하는 과정을 탐구해 보려고 한다. 우리가 시도하려는 변형은 직접 파이어폭스 소스코드를 변경한다. (주의 : 이 변형은 확장기능을 통해서도 할 수 있지만 그 과정은 다른 실습에서 다룰 것이다.). 그렇게 하므로서 우리는 최 상위 UI기능 변경에 대한 아이디어를 실제로 어떻게 구현하는 지를 (코드를 검색하여 분석하고 변경하여 테스트하는 과정을 통해) 배우게 될 것이다..

이번 연습의 목적은 여러분에게 이와 유사한 여러분 자신의 아이디어를 구현하는 것에 대한 확신을 주기 위해 또 몇 가지 유용한 테크닉을 알려 주고 모질라와 공개 소스에  있는 코드를 이용하는 것의 중요성을 강조하기 위함이다.

소스 코드 얻기

이번 연습에서 나열된 소스코드는 모두 mozilla-central revision f4800de50e03 에서 가져온 것들이다. 여기에는 최근의 변경들이 모두 포함되어 있다. 따라서 (command 창에서 ) 아래의 명령을 실행하여 동일한 버전을 다운받기 바란다.

  1. hg clone http://hg.mozilla.org/mozilla-central/
  2. hg update f4800de50e03
  3. (만일 버그 (bug 478871) 때문에 빌드(build)에 문제가 발생한다면 대신       4aed53dcf692 버전을 이용하기 바란다.)

‘무엇을’: 파이어폭스에서 새로운 탭을 생성하는 방법을 변형

지금 바로 파이어폭스에서 새로운 탭을 생성해보라, 그 탭은 현재 열려있는 탭들의 가장 마지막 끝에서 열린다. 하지만 누군가 ‘현재 탭’과 연관된 작업을 위해 새탭을 연다면 ,(즉,지금 6번째 탭에서 작업하고 있으며 새로운 탭을 마지막 위치가 아닌 현재 탭의 바로 다음 순서인 7번째 자리에 만들고 싶다면)

아래의 반복과정(the steps to reproduce)을 실행해보라 (버그질라에서는 특히 STR 이라고 줄여서 부른다.)

  1. 파이어폭스를 실행하여 일련의 탭을 연다. (또는 , CTRL+T 를 누른다.)
  2. 첫 번째 탭으로 이동한다.
  3. 새로운 탭을 열고 그 위치를 주시해보라 ( 틀림없이 마지막 위치가 될 것이다.)

‘어디에서’: 코드변경을 실행할 올바른 위치를 찾는다.

한편으로는 우리가 브라우저의 동작을 변경시키고자 하는 곳이지만, 다르게 말하면, 실제로 그것이 실행되는 곳이다. 우리 마음속에 가지고 있는 변화는 종국적으로는 아주 간단한 것이다. 하지만 그 간단한 작업코드가 있어야 할 곳이 어디인지 찾아내야 만한다. 어려울 수는 있지만 그렇다고 불가능한 것은 아니다.

어떻게 시작할까? 우선 최상 위에서 코드 안에 있는 어떤 UI 표현을 검색해보자 . 여기서 , 새로운 탭을 생성하는 여러가지 방법들에 대해 생각해보자.

  • ‘CTRL+T’ 누르기
  • 열린 탭에서 우측 클릭한 다음 ‘New Tab’을 선택
  • 메뉴 바에서 ‘File’ > ‘New Tab’ 선택

두 번째 세 번째 방법에서 우리가 코드에서 검색해야 할 ‘유용한 문자열’을 알 수 있다. 우선 무언가를 변경하기 전에 먼저 현재코드의 검색을 통해 코드를 분석하고 이해하여 어디에서 부터 시작해야 할지를 이해해야한다. –이것은 공개소스와 모질라 개발의 표준 패턴이다.

 

검색 1 – UI 문자열 찾기

독특한 문자열–“New Tab” 를 검색한다. —MXR’s 에서 문자검색 기능을 이용한다.

주소창에서 아래 주소를 넣고 실행하면 “New Tab”:검색을 실행한 결과를 얻을 수 있다

http://mxr.mozilla.org/mozilla-central/search?string=New+Tab

(2015.10.13.현재 기준으로 올바른 결과를 얻기 위해서는  위 URL 에서 “mozilla-central” 은 “l10n-central”로 바뀌어야한다. 이하 모든 MXR 관련 URL 동일)

결과물 중에 많은 것들은 코드내에 있는 설명(comments)들이다.하지만 tabbrowser.dtd 의 결과물은 흥미를 끈다. :

<!ENTITY newTab.label    “New Tab”>

여기서 우리는  DTD 파일이 ‘key/value’ 쌍을 en-US 지역화 문자열로 서술하고 있다는 것을 알수 있다. 모질라는 이러한 테크닉을 이용하여 코드 내에서 직접 코드를 바꾸지 않고 로컬라이져(지역화 모듈, localizers) 로 하여금  문자열을 각 지역의 언어로 번역이 가능하게 해준다. (다음 링크에서 ‘지역화’, DTD, Entities 에 관한 더 상세한 내용을 볼 수 있다. 다음 )

tabbrowser.dtd 파일을 자세히 살펴보면 영문 문자열 “New Tab”이 엔터티( ENTITY) 로 newTab.label를 이용한다는 것을 알 수 있다.

이것은 우리에게 특정 문자열 대신에 특정entity 를 다시 검색할 수 있게 해주는 것이므로 아주 훌륭한 정보가 되며 우리가 접근해야 할 코드에 좀더 가까이 갈 수 있게 도와준다.

(번역자 주: 우리의 관심은 검색대상 문자열 ‘New Tab’에 연계된 엔터티 명을 찾는 것이다.  )

검색 2 ENTITY 찾기

아래와 같이 문자열 “New Tab” 대신에 ENTITY 값 newTab.label 를 다시 검색한 결과는 많이 다르다. – 몇 개 안되는 내용이 얻어진다. :

http://mxr.mozilla.org/mozilla-central/search?string=newTab.label

놀랄 것도 없이 첫 번째 결과는 앞에서 우리가 얻은 것과 동일한 DTD 파일 (i.e., tabbrowser.dtd) 에서 얻어진 것이다.

tabbrowser.xml 파일에서 흥미로운 것을 알 수 있다.:

(번역자 주: 이러한 검색 결과를 얻으려면 현재로서는 검색대상 디렉토리에서  ‘ /mozilla-central/’ 이 아니라 ‘/common-central/’ 를 이용해야 한다. 하지만 이것도 완벽하게 일치하지는 않는다. 위에서 추천하는 리비전 f4800de50e03 를 다운받아 검색해보면 이 글과 완전히 동일한 결과를 얻을 수 있음이 확인되는 바, 이 글이 쓰여진 이후로 버전이 많이 바뀐 때문이라고 판단된다. 

해결방법: PRGreg를 다운받아 컴퓨터에 설치하면 MXR 서버 대신에  컴퓨터에 내려 받은 동일 리비젼 의 소스코드를 내용검색 할 수 있고, 검색 결과도 모질라 서버 검색결과와 거의 유사하게 얻을 수 있다. )

<xul:menuitem id=”context_newTab” label=”&newTab.label;” accesskey=”&newTab.accesskey;”             xbl:inherits=”oncommand=onnewtab”/>

여기서 우리는 ‘new Tab’ 을 위한 ‘the pop-up context menu’ (i.e., 탭위에서 우측 클릭하여 얻어지는 ) 의 정의를 볼 수 있다. 또한 적당한 엔터티 값을 얻게 됨으로서 함수 onnewtab 이 사용되고 있다는 것도 알게 되었다. 이 코드 줄은 xul:menuitem 이 oncommand 값을 부모로부터 상속받는다는 것도 알려준다. (다음 링크에서 XBL 속성(attribute)의 상속에 관해 더 자세한 것을 알 수 있다. 다음). 다시 말하면 이 메뉴가 클릭되면 onnewtab 함수가 불려진다는 것이다.

검색 3 함수찾기

이러한 새로운 정보로 무장하고나면 우리는 작업을 시작해야할 올바른 장소를 찾는 것에 좀더 가까워져 있게된다. 우리는 UI 문자열에서 시작해서 XML ENTITY 를거쳐 함수에까지 도달해있다. 이제 우리개해야할 일은 그 함수를 찾는 것이다.:

http://mxr.mozilla.org/mozilla-central/search?string=onnewtab

이 주소를 접속하면 결과는 /DB(디렉토리)에 뿌리를 둔 파일들이 포함된 그리 흥미롭지 않은 것들을 얻게 된다. 우리가 관심있는 것은 파이어폭스에서의 동작을 찾는 것이기 때문에 /browser 디렉토리에 뿌리를둔 파일들에 집중할 필요가 있다. 특별하게 관심이 가는 파일이 있다. : browser.xul

onnewtab=”BrowserOpenTab();”

이 경우에 tabbrowser widget 은 또 다른 함수 BrowserOpenTab() 로 정의된 onnewtab 이라는 특성을 가진다. (i.e., 파이어폭스는 일반적인 경우보다 비용이 많이 드는 방식으로 탭을 생성하는 것처럼 보인다. ). 이제 우리는 이 함수의 정의를 찾고자 하는 것이므로 함수 BrowserOpenTab(“를 검색하자. :

http://mxr.mozilla.org/mozilla-central/search?string=function%20BrowserOpenTab(

Now we’ve drilled down to the point where we have only one result, the function itself in browser.js:

이제 함수 자체는 browser.js 파일 속에 있다는 오직 한가지 결론을 파헤쳐보자.:

function BrowserOpenTab()

{

if (!gBrowser) {

// If there are no open browser windows, open a new one

window.openDialog(“chrome://browser/content/”, “_blank”,

“chrome,all,dialog=no”, “about:blank”);

return;

}

gBrowser.loadOneTab(“about:blank”, null, null, null, false, false);

if (gURLBar)

gURLBar.focus();

}

여기서 우리는 loadOneTab()라는 또 다른 함수를 볼 필요가 있다는 것을 알게 된다.

검색 한다.

http://mxr.mozilla.org/mozilla-central/search?string=loadOneTab

첫 번째 것은 놀라운 것도 아니므로 tabbrowser widget으로 되돌아가 보자.

우리가 실제로 원하는코드가 tabbrowser.xml 파일 속에 있다::.

<method name=”loadOneTab”>

<parameter name=”aURI”/>

<parameter name=”aReferrerURI”/>

<parameter name=”aCharset”/>

<parameter name=”aPostData”/>

<parameter name=”aLoadInBackground”/>

<parameter name=”aAllowThirdPartyFixup”/>

<body>

<![CDATA[

var bgLoad = (aLoadInBackground != null) ? aLoadInBackground :

this.mPrefs.getBoolPref(“browser.tabs.loadInBackground”);

var owner = bgLoad ? null : this.selectedTab;

var tab = this.addTab(aURI, aReferrerURI, aCharset, aPostData, owner,

aAllowThirdPartyFixup);

if (!bgLoad)

this.selectedTab = tab;

return tab;

]]>

</body>

</method>

코드가 XML 파일 안에 있는 것이 놀라울 수도 있다. tabbrowser 코드는 재 사용 가능한 위젯을 생성하며 그 정의와 기능성이 그 곳에 정의 되어있다. 파이어폭스는 탭 생성 기능 지원을 받기 위해 tabbrowser를 이용한다 .  loadOneTab 와 같은 함수(메소드) 는 이런 위젯의 일부이며 여기에서 정의되는 것이다.

하지만 우리의 검색은 끝이 아니다. loadOneTab 메소드(함수)는 이제 진짜 새로운 탭을 생성해서 삽입해주는 또 다른 함수를 호출한다.:

var tab = this.addTab(aURI, aReferrerURI, aCharset, aPostData, owner, aAllowThirdPartyFixup);

  은 그런 함수 중의 하나 이므로 우리는 그 정의가 그 파일 속에 포함되어 있을 것이라고 쉽게  예상할 수 있다. 그 파일을 검색해보면 마침내 올바른 장소를 찾을 수 있다.

this.mTabContainer.appendChild(t);

이제 우리가 할 일은 끝자리에 붙이는 대신 중간에 끼워 넣기 하기 위해 코드를 수정하는 것이다.

‘어떻게’: 코드 수정의 필요성

이러한 변경을 하기위해  몇가지 다른 방법이 있다. 여기서 우리에게는 새로운 방식으로 코드를 변경해보자.  물론 같은 결과를 얻을 수 있는 더 좋은 방법이 있을 수도 있다. –코드를 들여다 보면 알 수 있음. 하지만 공부를 위한 목적으로 우리에게 최적으로 보이는 방법을 계속 유지하는 것이다. (실습을 두려워 하지 말고 시도해보자). 우리가 만들어낼 실수들을 통해서 어떻게 그것을 탐지하고 수정해가는 지를 논의하게 될 것이다.

첫 번째 시도

기존의 코드는 아주 잘 작동되는 것이므로 목표는 가능한 최소한의 변경이다. –아주 조금만 다른 동작을 하게하는 것을 원한다. 또한 그런 작은 변경을 위해서 코드 전체를 들여다보는 것에 관심을 두는 것도 아니다 . 우리가 이미 잘 할 수 있는 것을 활용한 가능한 최소의 변경을 원한다

appendChild() 메소드가 우리가 원하는 변경작동(이름하여 목록의 끝에 새로운 탭을 생성하는 )에 관련있다고 가정하자 . 우리는 무엇을 바꿔야 하는지 확신하지 못하고 있으므로 tabbrowser.xml 파일에서 또 다른 검색을 해야한다. (i.e.,CTRL+F 키를 이용하여 ) mTabContainer 의 메소드 / 속성을 찾아야한다. 조금 관심이 가는 선택지를 찾게 된다:

index = this.mTabContainer.selectedIndex;

this.mTabContainer.insertBefore(aTab, this.mTabContainer.childNodes.item(aIndex));

var position = this.mTabContainer.childNodes.length-1;

이것만으로 우리의 목적을 달성할 수 있을 것이라는 결론을 얻게되어, 작업을 개시한다.  이것이 첫 번째 시도이다. 아래에 browser/base/content/tabbrowser.xml 파일과 addTab 메소드 변경 내용을 보여준다.:

// Insert tab after current tab, not at end.

if (this.mTabContainer.childNodes.length == 0) {

this.mTabContainer.appendChild(t);

} else {

var currentTabIndex = this.mTabContainer.selectedIndex;

this.mTabContainer.insertBefore(t, currentTabIndex + 1);

}

이제 브라우저 패키지의 .jar 파일에 우리가 한 변경을 반영하기 위해 리빌드할 필요가 있다.  :

$ make -C objdir/browser (objdir 를 자신의 objdir 이름으로 바꾼다.)

그 다음 , 새로 생성된 브라우저 테스트를 위해 run 명령을 실행한다. (만일 다른 버전의 파이어폭스가 실행 되고 있다면 -no-remote 를 활용하라. 또한 테스트 프로파일을 생성하여 이용하고자한다면 –profilemanager 를 이용한다). 만일 윈도즈환경에서 소스코드가 C:\mozilla-central\src 에 위치하고 있다면:

$ export XPCOM_DEBUG_BREAK=warn

이것은 윈도즈에서 디버그할 때 팝업 대화창의 생성을 막아준다.

$ C:\mozilla-central\src\objdir\dist\bin\firefox.exe -profilemanager -no-remote

파일 경로는 자신의 것(objdir)으로 바꿔 주어야한다.

챗번째 시도에서 어떤 결과를 얻었는가? File > New Tab 선택을 해보 새탭을 생성해보라.

두 번째 시도

addTab 이 완전히 망가진 경우라면 이 코드는 어떤 문제가 있음이 명백하다. . Error Console (Tools > Error Console). 에서 단서를 찾아보자 새로운 탭을 생성하려할 때면 언제나 다음과 같은 에외 메시지가 나타나는 것을 볼 수 있다.:

Error: uncaught exception: [Exception… “Could not convert JavaScript argument”nsresult: “0x80570009 (NS_ERROR_XPC_BAD_CONVERT_JS)” location: “JS frame ::chrome://global/content/bindings/tabbrowser.xml :: addTab :: line 1161” data: no]

우리는 자바스크립트 실행 결과의 에러를 어떻게 찾아내는지 알고있다. 에러 메시지에 표시된 라인 넘버보다 실제 소스 파일에서의 라인 넙버가 더 클수도있다. (새로 생성된 파이어폭스에서 chrome://global/content/bindings/tabbrowser.xml 파일을 다운받아 이걸 살펴보자). 오류는 다른 절에서 발생한 것이므로, 거기서의 childNodes.length 값은 기본적으로 영이 아니고 1이다. (i.e., 모든 브라우저 창은 탭 ,조절자가 보이지 않는 경우라고 해도 ,적어도 한개 이상의 탭을 가지고 있으므로). 재빨리 코드를 수정한다. t:

if (this.mTabContainer.childNodes.length == 1) { …

세번째 시도 t

이 작업은 오직 내가 새로운 탭을 생성하는 경우이다. 분명한 것은 우리가 아직도 mTabContainer.selectedIndexmTabContainer.insertBefore()mTabContainer.selectedIndexmTabContainer.insertBefore() 가 실제로 어떻게 작동하는지에 대해 약간 잘못된 개념을 갖고 있다는 것이다.

우리는 코드가 어떤 잘못이 있는지 아직 보지 못했지만 오류 메시지는 분명히 변환 문제와 유사한 종류라는것을 나타내고 있다. 이제 다시 특히 insertChild() 와 같은 메소드와 특성을 줄 곧 사용하는 tabbrowser.xml 파일을 들여다보기로 한다.

수초이내에 오류가 드러난다: 탭이 필요한 곳에서 인티저를 사용해왔다는 것이다. 아래는 수정된 코드이다. :

// Insert tab after current tab, not at end.if (this.mTabContainer.childNodes.length == 1) { this.mTabContainer.appendChild(t);} else { var currentTabIndex = this.mTabContainer.selectedIndex; this.mTabContainer.insertBefore(t, this.mTabContainer.childNodes.item(currentTabIndex + 1));}

기대한 결과와 약간의 오류

다시 빌드해서 실행해보면 , 이번 마지막 변경이 성공적이라는 것을 확인해준다. l. 새창열기 작업은 이제 우리가 처음에 설명했던 대로 작동한다. 우리가 실패한것이 아니라는것을 확신하기위해 몇번의 테스트를 더 진행하자. 에를 들자면 중간 위치가 아닌 마지막 탭 위치에서 작동하면 어떻게 되는지 등등 .이런 작업은 구지 append() 명령을 쓸 필요가 없다는 것을 알 수 있으며 아래처럼 더 간단하고 안전하게 바꿀 수 있다:

// Insert tab after current tab, not at end.var currentTabIndex = this.mTabContainer.selectedIndex;this.mTabContainer.insertBefore(t, this.mTabContainer.childNodes.item(currentTabIndex + 1));

이는 여엇 줄이 두줄로 줄어들게되고 결국 새로운 버그를 만들어 낼 가능성이 감소한다는 것을 의미한다.

버그에 대해서 샐펴보자, addTab새로 추가한 position 를 자세히 들여다보면:

// wire up a progress listener for the new browser object.var position = this.mTabContainer.childNodes.length-1;var tabListener = this.mTabProgressListener(t, b, blank);…this.mTabListeners[position] = tabListener;this.mTabFilters[position] = filter;…t._tPos = position;

이것은 새로 생성된 탭의 내부 위치(position)에 오류가 있기 때문에 다른 것들 사이에있는 탭이 삭제를 방해한다. 전에는 새로운 탭은 마지막에 생성된다는 가정을 했기 새로 넣은 코드는 이것을 막는 것이다 . 따라서 position 값을 새로 설정할 필요가있다.

// wire up a progress listener for the new browser object.var position = currentTabIndex + 1

우리가 한 변경에의해 분명히 눈에 보이는 오류는 없지만 그렇다고 해서 버그가 완전히 없다고 말할 수는 없다. 다른 이가 테스트해보면 우리가 놓치고간 오류를 찾아낼 수도 있다.

감상 (토론, 느낀점)

이제 우리가 해본 실습은 다른 어떤 문서를 보여주거나 자바스크립트 디버거를 사용하는 것을 방해하지 않을 만큼 간단하다. 탭 브라우져(tabbrowser)에 관한 훌륭한 참고문서를 여기 소개한다 ( documentation for tabbrowser).

우리는 여기서 중요한 테크닉을 배웠다. : 현재의 코드는 새로운 코드를 작성하기 위한 최선의 방법이다. 이는 모질라 프로젝트의 코딩 스타일을 이해하고 그 위치를 파악하고, 호출할 메소드나 재 사용할 수 있는 특성을 찾아내는 것 등에 도움을 준다. 모질라 플랫폼의 가치는 광범위하고 강력하다는것 뿐만이 아니라 공개되어 있고 우리가 이것을 공부하는 것이 무료라는 점이다.

소셜댓글

Loading Disqus Comments ...

답글 남기기

이메일 주소는 공개되지 않습니다. 필수 필드는 *로 표시됩니다

이 사이트는 스팸을 줄이는 아키스밋을 사용합니다. 댓글이 어떻게 처리되는지 알아보십시오.